Merge branch 'stable-2.8'

* stable-2.8:
  SideBySide2: Revise SkipBar and disable horizontal scroll on LineWidgets
  Update CodeMirror3
diff --git a/.gitmodules b/.gitmodules
index 6476c4c..d75c98c 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -18,3 +18,6 @@
 	path = plugins/reviewnotes
 	url = ../plugins/reviewnotes
 
+[submodule "plugins/singleusergroup"]
+	path = plugins/singleusergroup
+	url = ../plugins/singleusergroup
diff --git a/BUCK b/BUCK
index 616a0fe..c78bc2c 100644
--- a/BUCK
+++ b/BUCK
@@ -3,14 +3,16 @@
 gerrit_war(name = 'gerrit')
 gerrit_war(name = 'chrome',   ui = 'ui_chrome')
 gerrit_war(name = 'firefox',  ui = 'ui_firefox')
-gerrit_war(name = 'withdocs', context = DOCS)
-gerrit_war(name = 'release',  context = DOCS + ['//plugins:core.zip'])
+gerrit_war(name = 'withdocs', docs = True)
+gerrit_war(name = 'release',  docs = True, context = ['//plugins:core.zip'])
 
 API_DEPS = [
   ':extension-api',
   ':extension-api-src',
   ':plugin-api',
   ':plugin-api-src',
+  ':plugin-gwtui',
+  ':plugin-gwtui-src',
 ]
 
 genrule(
@@ -76,3 +78,19 @@
   ] + [d + '-src' for d in PLUGIN_API],
   visibility = ['//tools/maven:'],
 )
+
+genrule(
+  name = 'plugin-gwtui',
+  cmd = 'ln -s $(location //gerrit-plugin-gwtui:client) $OUT',
+  deps = ['//gerrit-plugin-gwtui:client'],
+  out = 'plugin-gwtui.jar',
+  visibility = ['//tools/maven:'],
+)
+
+genrule(
+  name = 'plugin-gwtui-src',
+  cmd = 'ln -s $(location //gerrit-plugin-gwtui:src) $OUT',
+  deps = ['//gerrit-plugin-gwtui:src'],
+  out = 'plugin-gwtui-src.jar',
+  visibility = ['//tools/maven:'],
+)
diff --git a/Documentation/BUCK b/Documentation/BUCK
index 71d8664..b2b7d2a 100644
--- a/Documentation/BUCK
+++ b/Documentation/BUCK
@@ -3,7 +3,6 @@
 include_defs('//tools/git.defs')
 
 DOC_DIR = 'Documentation'
-INDEX_DIR = DOC_DIR + '/.index'
 MAIN = ['//gerrit-pgm:pgm', '//gerrit-gwtui:ui_module']
 SRCS = glob(['*.txt'], excludes = ['licenses.txt'])
 
@@ -11,12 +10,10 @@
   name = 'html',
   cmd = 'cd $TMP;' +
     'mkdir -p %s/images;' % DOC_DIR +
-    'unzip -q $SRCDIR/index.zip -d %s/;' % INDEX_DIR +
     'unzip -q $SRCDIR/only_html.zip -d %s/;' % DOC_DIR +
     'for s in $SRCS;do ln -s $s %s;done;' % DOC_DIR +
     'mv %s/*.{jpg,png} %s/images;' % (DOC_DIR, DOC_DIR) +
     'rm %s/only_html.zip;' % DOC_DIR +
-    'rm %s/index.zip;' % DOC_DIR +
     'rm %s/licenses.txt;' % DOC_DIR +
     'cp $SRCDIR/licenses.txt LICENSES.txt;' +
     'zip -qr $OUT *',
@@ -27,11 +24,9 @@
       'doc.css',
       genfile('licenses.txt'),
       genfile('only_html.zip'),
-      genfile('index.zip'),
     ],
   deps = [
     ':generate_html',
-    ':index',
     ':licenses.txt',
   ],
   out = 'html.zip',
@@ -79,3 +74,18 @@
   ],
   out = 'index.zip',
 )
+
+genrule(
+  name = 'index_jar',
+  cmd = 'jar cf $OUT -C $SRCDIR index.zip',
+  srcs = [genfile('index.zip')],
+  deps = [':index'],
+  out = 'index.jar',
+)
+
+prebuilt_jar(
+  name = 'index_lib',
+  binary_jar = genfile('index.jar'),
+  deps = [':index_jar'],
+  visibility = ['PUBLIC'],
+)
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 189316c..f8d4308 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -425,7 +425,7 @@
 
 
 [[category_create]]
-Create reference
+Create Reference
 ~~~~~~~~~~~~~~~~
 
 The create reference category controls whether it is possible to
@@ -1340,3 +1340,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-apropos.txt b/Documentation/cmd-apropos.txt
new file mode 100644
index 0000000..29ce9fa
--- /dev/null
+++ b/Documentation/cmd-apropos.txt
@@ -0,0 +1,66 @@
+gerrit apropos
+==============
+
+NAME
+----
+gerrit apropos - Search Gerrit documentation index
+
+SYNOPSIS
+--------
+[verse]
+'ssh' -p <port> <host> 'gerrit apropos'
+  <query>
+
+DESCRIPTION
+-----------
+Queries the documentation index and returns results with the title and URL
+from the matched documents.
+
+ACCESS
+------
+Any user who has configured an SSH key.
+
+SCRIPTING
+---------
+This command is intended to be used in scripts.
+
+Note: this feature is only available if documentation index was built.
+
+EXAMPLES
+--------
+
+=====
+$ ssh -p 29418 review.example.com gerrit apropos capabilities
+    Gerrit Code Review - /config/ REST API:
+    http://localhost:8080/Documentation/rest-api-config.html
+
+    Gerrit Code Review - /accounts/ REST API:
+    http://localhost:8080/Documentation/rest-api-accounts.html
+
+    Gerrit Code Review - Project Configuration File Format:
+    http://localhost:8080/Documentation/config-project-config.html
+
+    Gerrit Code Review - Access Controls:
+    http://localhost:8080/Documentation/access-control.html
+
+    Gerrit Code Review - Plugin Development:
+    http://localhost:8080/Documentation/dev-plugins.html
+
+    Gerrit Code Review - REST API:
+    http://localhost:8080/Documentation/rest-api.html
+
+    Gerrit Code Review - /access/ REST API:
+    http://localhost:8080/Documentation/rest-api-access.html
+=====
+
+SEE ALSO
+--------
+
+* link:access-control.html[Access Controls]
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-ban-commit.txt b/Documentation/cmd-ban-commit.txt
index fb4a2ac9..fcdad5c 100644
--- a/Documentation/cmd-ban-commit.txt
+++ b/Documentation/cmd-ban-commit.txt
@@ -58,3 +58,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-cherry-pick.txt b/Documentation/cmd-cherry-pick.txt
index 15a8524..1275d20ea 100644
--- a/Documentation/cmd-cherry-pick.txt
+++ b/Documentation/cmd-cherry-pick.txt
@@ -45,3 +45,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-create-account.txt b/Documentation/cmd-create-account.txt
index 3ecb764..f22b4df 100644
--- a/Documentation/cmd-create-account.txt
+++ b/Documentation/cmd-create-account.txt
@@ -81,3 +81,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-create-group.txt b/Documentation/cmd-create-group.txt
index 8dc6dcc..1102a6d 100644
--- a/Documentation/cmd-create-group.txt
+++ b/Documentation/cmd-create-group.txt
@@ -92,3 +92,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-create-project.txt b/Documentation/cmd-create-project.txt
index e3ad834..da7820f 100644
--- a/Documentation/cmd-create-project.txt
+++ b/Documentation/cmd-create-project.txt
@@ -201,3 +201,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-flush-caches.txt b/Documentation/cmd-flush-caches.txt
index bc6fac5..050b433 100644
--- a/Documentation/cmd-flush-caches.txt
+++ b/Documentation/cmd-flush-caches.txt
@@ -104,3 +104,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-gc.txt b/Documentation/cmd-gc.txt
index 07b899a..3066ca0 100644
--- a/Documentation/cmd-gc.txt
+++ b/Documentation/cmd-gc.txt
@@ -10,6 +10,7 @@
 [verse]
 'ssh' -p <port> <host> 'gerrit gc'
   [--all]
+  [--show-progress]
   <NAME> ...
 
 DESCRIPTION
@@ -46,6 +47,9 @@
 	If specified the Git garbage collection is run for all projects
 	sequentially.
 
+--show-progress::
+	If specified progress information is shown.
+
 EXAMPLES
 --------
 
@@ -70,3 +74,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-gsql.txt b/Documentation/cmd-gsql.txt
index 3c1fd31..4efbb2a 100644
--- a/Documentation/cmd-gsql.txt
+++ b/Documentation/cmd-gsql.txt
@@ -65,3 +65,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-hook-commit-msg.txt b/Documentation/cmd-hook-commit-msg.txt
index c0c1e6c..c3ad1ac 100644
--- a/Documentation/cmd-hook-commit-msg.txt
+++ b/Documentation/cmd-hook-commit-msg.txt
@@ -117,6 +117,7 @@
 
 GERRIT
 ------
-
-
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-index.txt b/Documentation/cmd-index.txt
index 3ba66a8..a274dec 100644
--- a/Documentation/cmd-index.txt
+++ b/Documentation/cmd-index.txt
@@ -51,6 +51,9 @@
 [[user_commands]]User Commands
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+link:cmd-apropos.html[gerrit apropos]::
+	Search Gerrit documentation index.
+
 'gerrit approve'::
 	'Deprecated alias for `gerrit review`.'
 
@@ -180,3 +183,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-kill.txt b/Documentation/cmd-kill.txt
index f09053e..94371f0 100644
--- a/Documentation/cmd-kill.txt
+++ b/Documentation/cmd-kill.txt
@@ -28,3 +28,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-ls-groups.txt b/Documentation/cmd-ls-groups.txt
index 17ebba1..0713947 100644
--- a/Documentation/cmd-ls-groups.txt
+++ b/Documentation/cmd-ls-groups.txt
@@ -156,3 +156,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-ls-members.txt b/Documentation/cmd-ls-members.txt
index 9814ff2..d5f127f 100644
--- a/Documentation/cmd-ls-members.txt
+++ b/Documentation/cmd-ls-members.txt
@@ -62,3 +62,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-ls-projects.txt b/Documentation/cmd-ls-projects.txt
index 26530bd..5cd416d1 100644
--- a/Documentation/cmd-ls-projects.txt
+++ b/Documentation/cmd-ls-projects.txt
@@ -154,3 +154,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-ls-user-refs.txt b/Documentation/cmd-ls-user-refs.txt
index 25a99d1..26a083c 100644
--- a/Documentation/cmd-ls-user-refs.txt
+++ b/Documentation/cmd-ls-user-refs.txt
@@ -53,3 +53,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-plugin-enable.txt b/Documentation/cmd-plugin-enable.txt
index da651ca..ad1736d 100644
--- a/Documentation/cmd-plugin-enable.txt
+++ b/Documentation/cmd-plugin-enable.txt
@@ -42,3 +42,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-plugin-install.txt b/Documentation/cmd-plugin-install.txt
index 719c2bc..61663a6 100644
--- a/Documentation/cmd-plugin-install.txt
+++ b/Documentation/cmd-plugin-install.txt
@@ -71,3 +71,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-plugin-ls.txt b/Documentation/cmd-plugin-ls.txt
index 6cce83c..5e651b7 100644
--- a/Documentation/cmd-plugin-ls.txt
+++ b/Documentation/cmd-plugin-ls.txt
@@ -42,3 +42,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-plugin-reload.txt b/Documentation/cmd-plugin-reload.txt
index 3932e30..a4490a8 100644
--- a/Documentation/cmd-plugin-reload.txt
+++ b/Documentation/cmd-plugin-reload.txt
@@ -46,3 +46,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-plugin-remove.txt b/Documentation/cmd-plugin-remove.txt
index ab8f95b..f9c0eea 100644
--- a/Documentation/cmd-plugin-remove.txt
+++ b/Documentation/cmd-plugin-remove.txt
@@ -43,3 +43,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-query.txt b/Documentation/cmd-query.txt
index 66bd845..249b7f9 100644
--- a/Documentation/cmd-query.txt
+++ b/Documentation/cmd-query.txt
@@ -17,6 +17,7 @@
   [--commit-message]
   [--dependencies]
   [--submit-records]
+  [--all-reviewers]
   [--]
   <query>
   [limit:<n>]
@@ -89,6 +90,10 @@
 	Show information about patch sets which depend on, or are needed by,
 	each patch set.
 
+--all-reviewers::
+	Show the name and email of all reviewers which are added to a change
+	(irrespective of whether they have been voting on that change or not).
+
 --submit-records::
 	Show submit record information about the change, which
 	includes whether the change meets the criteria for submission
@@ -155,3 +160,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-receive-pack.txt b/Documentation/cmd-receive-pack.txt
index 92bb65e..239d2f5 100644
--- a/Documentation/cmd-receive-pack.txt
+++ b/Documentation/cmd-receive-pack.txt
@@ -88,3 +88,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-rename-group.txt b/Documentation/cmd-rename-group.txt
index e810727..624ac9b 100644
--- a/Documentation/cmd-rename-group.txt
+++ b/Documentation/cmd-rename-group.txt
@@ -44,3 +44,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-review.txt b/Documentation/cmd-review.txt
index 70213da..3038ead 100644
--- a/Documentation/cmd-review.txt
+++ b/Documentation/cmd-review.txt
@@ -134,3 +134,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-set-account.txt b/Documentation/cmd-set-account.txt
index f9855cd..19eb468 100644
--- a/Documentation/cmd-set-account.txt
+++ b/Documentation/cmd-set-account.txt
@@ -94,3 +94,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-set-members.txt b/Documentation/cmd-set-members.txt
index 7524893..d7da2eb 100644
--- a/Documentation/cmd-set-members.txt
+++ b/Documentation/cmd-set-members.txt
@@ -80,3 +80,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-set-project-parent.txt b/Documentation/cmd-set-project-parent.txt
index 1e7e6c5..5664890 100644
--- a/Documentation/cmd-set-project-parent.txt
+++ b/Documentation/cmd-set-project-parent.txt
@@ -71,3 +71,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-set-project.txt b/Documentation/cmd-set-project.txt
index af20006..df694a0 100644
--- a/Documentation/cmd-set-project.txt
+++ b/Documentation/cmd-set-project.txt
@@ -117,3 +117,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-set-reviewers.txt b/Documentation/cmd-set-reviewers.txt
index 32fd35e..81b493d 100644
--- a/Documentation/cmd-set-reviewers.txt
+++ b/Documentation/cmd-set-reviewers.txt
@@ -87,3 +87,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-show-caches.txt b/Documentation/cmd-show-caches.txt
index d426508..8c64b05 100644
--- a/Documentation/cmd-show-caches.txt
+++ b/Documentation/cmd-show-caches.txt
@@ -83,3 +83,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-show-connections.txt b/Documentation/cmd-show-connections.txt
index ab9fadf..154e1c2 100644
--- a/Documentation/cmd-show-connections.txt
+++ b/Documentation/cmd-show-connections.txt
@@ -88,3 +88,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-show-queue.txt b/Documentation/cmd-show-queue.txt
index 4ab3097..7364d2d 100644
--- a/Documentation/cmd-show-queue.txt
+++ b/Documentation/cmd-show-queue.txt
@@ -90,3 +90,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-stream-events.txt b/Documentation/cmd-stream-events.txt
index 2a3265e..746b3ea 100644
--- a/Documentation/cmd-stream-events.txt
+++ b/Documentation/cmd-stream-events.txt
@@ -172,3 +172,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-suexec.txt b/Documentation/cmd-suexec.txt
index 78fc361..7b128fa 100644
--- a/Documentation/cmd-suexec.txt
+++ b/Documentation/cmd-suexec.txt
@@ -67,3 +67,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-test-submit-rule.txt b/Documentation/cmd-test-submit-rule.txt
index ae68b80..466182c 100644
--- a/Documentation/cmd-test-submit-rule.txt
+++ b/Documentation/cmd-test-submit-rule.txt
@@ -66,3 +66,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-test-submit-type.txt b/Documentation/cmd-test-submit-type.txt
index f6d5fba..2b8790c 100644
--- a/Documentation/cmd-test-submit-type.txt
+++ b/Documentation/cmd-test-submit-type.txt
@@ -51,3 +51,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-version.txt b/Documentation/cmd-version.txt
index aa08848..806e28a 100644
--- a/Documentation/cmd-version.txt
+++ b/Documentation/cmd-version.txt
@@ -46,3 +46,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-auto-site-initialization.txt b/Documentation/config-auto-site-initialization.txt
index 4c204fd..6111d5d 100644
--- a/Documentation/config-auto-site-initialization.txt
+++ b/Documentation/config-auto-site-initialization.txt
@@ -80,3 +80,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-cla.txt b/Documentation/config-cla.txt
index 6404d4e..fa28e7b 100644
--- a/Documentation/config-cla.txt
+++ b/Documentation/config-cla.txt
@@ -80,3 +80,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-contact.txt b/Documentation/config-contact.txt
index 4d8851f..6c59d2a 100644
--- a/Documentation/config-contact.txt
+++ b/Documentation/config-contact.txt
@@ -213,3 +213,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 6d00dfa..1ceb3dc 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1372,7 +1372,7 @@
 [[gerrit.changeScreen]]gerrit.changeScreen::
 +
 Default change screen UI to direct users to. Valid values are
-`OLD_UI` and `CHANGE_SCREEN2`. Default is `OLD_UI`.
+`OLD_UI` and `CHANGE_SCREEN2`. Default is `CHANGE_SCREEN2`.
 
 [[gitweb]]Section gitweb
 ~~~~~~~~~~~~~~~~~~~~~~~~
@@ -3031,3 +3031,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-gitweb.txt b/Documentation/config-gitweb.txt
index 7ba15b8..3e9cb96 100644
--- a/Documentation/config-gitweb.txt
+++ b/Documentation/config-gitweb.txt
@@ -245,6 +245,10 @@
 After updating `'$site_path'/etc/gerrit.config`, the Gerrit server must
 be restarted and clients must reload the host page to see the change.
 
+Note that when using a custom gitweb configuration, values must be
+specified for all of the `project`, `revision`, `branch` and `filehistory`
+settings, otherwise the configuration will not be used.
+
 Access Control
 ++++++++++++++
 
@@ -278,3 +282,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-hooks.txt b/Documentation/config-hooks.txt
index 5875837..f9a498b 100644
--- a/Documentation/config-hooks.txt
+++ b/Documentation/config-hooks.txt
@@ -169,3 +169,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-labels.txt b/Documentation/config-labels.txt
index c08d484..ba3d482 100644
--- a/Documentation/config-labels.txt
+++ b/Documentation/config-labels.txt
@@ -315,3 +315,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-login-register.txt b/Documentation/config-login-register.txt
index 867f0d4..d3911d7 100644
--- a/Documentation/config-login-register.txt
+++ b/Documentation/config-login-register.txt
@@ -141,3 +141,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-mail.txt b/Documentation/config-mail.txt
index 3b8bffa..ca9253ee 100644
--- a/Documentation/config-mail.txt
+++ b/Documentation/config-mail.txt
@@ -211,3 +211,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-project-config.txt b/Documentation/config-project-config.txt
index 8529b67..3267c45 100644
--- a/Documentation/config-project-config.txt
+++ b/Documentation/config-project-config.txt
@@ -223,3 +223,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-reverseproxy.txt b/Documentation/config-reverseproxy.txt
index 064fe2e..563b322 100644
--- a/Documentation/config-reverseproxy.txt
+++ b/Documentation/config-reverseproxy.txt
@@ -147,3 +147,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-sso.txt b/Documentation/config-sso.txt
index e915ffb..1d8d2d6 100644
--- a/Documentation/config-sso.txt
+++ b/Documentation/config-sso.txt
@@ -179,3 +179,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-themes.txt b/Documentation/config-themes.txt
index c102381..5eb8094 100644
--- a/Documentation/config-themes.txt
+++ b/Documentation/config-themes.txt
@@ -140,3 +140,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-validation.txt b/Documentation/config-validation.txt
index 1b09d19..7ef646b 100644
--- a/Documentation/config-validation.txt
+++ b/Documentation/config-validation.txt
@@ -40,3 +40,6 @@
 ------
 Part of link:index.html[Gerrit Code Review]
 
+
+SEARCHBOX
+---------
diff --git a/Documentation/database-setup.txt b/Documentation/database-setup.txt
index 3800473..1f48f53 100644
--- a/Documentation/database-setup.txt
+++ b/Documentation/database-setup.txt
@@ -113,3 +113,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-buck.txt b/Documentation/dev-buck.txt
index 15c145a..c2a4584 100644
--- a/Documentation/dev-buck.txt
+++ b/Documentation/dev-buck.txt
@@ -175,7 +175,7 @@
 To build a specific plugin:
 
 ----
-  buck build plugins/<name>
+  buck build plugins/<name>:<name>
 ----
 
 The output JAR file will be be placed in:
@@ -187,6 +187,22 @@
 Note that when building an individual plugin, the `core.zip` package
 is not regenerated.
 
+Additional plugins with BUCK files can be added to the build
+environment by cloning the source repository into the plugins
+subdirectory:
+
+----
+  git clone https://gerrit.googlesource.com/plugins/<name> plugins/<name>
+  echo /plugins/<name> >>.git/info/exclude
+----
+
+Additional plugin sources will be automatically added to Eclipse the
+next time project.py is run:
+
+----
+  tools/eclipse/project.py
+----
+
 
 [[documentation]]
 Documentation
@@ -374,3 +390,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt
index edc072d..24fc266 100644
--- a/Documentation/dev-contributing.txt
+++ b/Documentation/dev-contributing.txt
@@ -288,3 +288,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-design.txt b/Documentation/dev-design.txt
index 3cb58b1..06dd95d 100644
--- a/Documentation/dev-design.txt
+++ b/Documentation/dev-design.txt
@@ -739,3 +739,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt
index d2fc8f0..eeee5a3 100644
--- a/Documentation/dev-eclipse.txt
+++ b/Documentation/dev-eclipse.txt
@@ -102,3 +102,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-inspector.txt b/Documentation/dev-inspector.txt
new file mode 100644
index 0000000..14177f8
--- /dev/null
+++ b/Documentation/dev-inspector.txt
@@ -0,0 +1,302 @@
+Gerrit Inspector
+================
+
+NAME
+----
+Gerrit Inspector - Interactive Jython environment for Gerrit
+
+SYNOPSIS
+--------
+[verse]
+'java' -jar gerrit.war 'daemon'
+	-d <SITE_PATH>
+	[\--enable-httpd | \--disable-httpd]
+	[\--enable-sshd | \--disable-sshd]
+	[\--console-log]
+	[\--slave]
+	-s
+
+DESCRIPTION
+-----------
+Runs the Gerrit network daemon on the local system as described
+in the link:pgm-daemon.html[Daemon documentation], additionally
+starting an interactive Jython shell for inspection
+and troubleshooting of live data of the Gerrit instance.
+
+CAUTION: Gerrit Inspector works directly on instances of Java Virtual
+Machine objects and it is possible to read and write instance
+members as well as invoke Java functions. Access is granted
+also to 'private' and 'protected' members. Therefore it is possible
+to introduce changes to the internal state of the system in
+an inconsistent way. Care must be taken not to break the running system
+and/or destroy the data.
+
+INSTALLATION
+------------
+
+Gerrit Inspector requires Jython library ('jython.jar') to be installed
+in the '$site_path/lib' directory. Jython, a Python interpreter for
+the Java Virtual Machine, can be obtained from the http://www.jython.org/
+website. Only 'jython.jar' file is needed, installation of Jython libraries
+is optional. Gerrit Inspector has been tested with Jython 2.5.2 but
+might work an earlier version.
+
+STARTUP
+-------
+
+During startup Jython examines Java libraries found on the classpath.
+While libraries are inspected a large amount of messages is displayed on the console:
+
+----
+*sys-package-mgr*: processing new jar, '/home/user/.gerritcodereview/tmp/gerrit_4890671371398741854_app/sshd-core-0.5.1-r1095809.jar'
+----
+
+After this a system-wide embedded initialization script is started. This script
+is contained in the gerrit's WAR archive. This script produces output similar to
+the following on the console:
+
+----
+"Shell" is "com.google.gerrit.pgm.shell.JythonShell@61644f2d"
+"m" is "com.google.gerrit.lifecycle.LifecycleManager@6f03b248"
+"ds" is "com.google.gerrit.server.schema.DataSourceProvider@6b3592c"
+"schk" is "com.google.gerrit.server.schema.SchemaVersionCheck@5e8cb9bd"
+
+Welcome to the Gerrit Inspector
+Enter help() to see the above again, EOF to quit and stop Gerrit
+----
+
+Then an optional user startup script is processed. It should be
+located in the gerrit user home directory as '.gerritcodereview/Startup.py'.
+
+This script can access all variables defined in the system (such
+as the ones displayed by the initialization script as shown above).
+Variables and functions defined by the startup scripts are available for
+the interactive interpreter.
+
+When interactive interpreter exits (by issuing EOF on the command line),
+a whole Gerrit instance is shut down gracefully.
+
+USING THE INTERPRETER
+---------------------
+
+Gerrit Inspector launches Jython interpreter in the context of the Gerrit
+Java Virtual Machine. All core facilities of the Jython (and Python)
+language are available to the user.
+
+Additional facilities can be provided, for example a 'Lib' directory from the
+Jython distribution can be installed under '$site_path/lib/Lib' to provide
+access to many standard Python modules. Jython can also use additional Java
+classes and libraries and most of the Python modules and scripts.
+
+The Inspector has by default access to classes and object instances available
+in the Java Virtual Machine. Objects are introspected and *private* and *protected*
+members are also available.
+
+For more information on using Jython, especially with regards to its limitations
+in interfacing to the Java Virtual Machine, please refer to the
+http://www.jython.org/[Jython documentation].
+
+After sucessful initialization it is possible to examine components of
+Java packages, classes and live instances.
+
+----
+>>> import com.google.inject
+>>> dir(com.google.inject)
+['AbstractModule', 'Binder', 'Binding', 'BindingAnnotation', 'ConfigurationException', 'CreationException', 'Exposed', 'Guice', 'ImplementedBy', 'Inject', 'Injector', 'Key', 'MembersInjector', 'Module', 'OutOfScopeException', 'PrivateBinder', 'PrivateModule', 'ProvidedBy', 'Provider', 'Provides', 'ProvisionException', 'Scope', 'ScopeAnnotation', 'Scopes', 'Singleton', 'Stage', 'TypeLiteral', '__name__', 'assistedinject', 'binder', 'internal', 'matcher', 'name', 'servlet', 'spi', 'util']
+>>> type(com.google.inject)
+<type 'javapackage'>
+>>> dir(com.google.inject.Guice)
+['__class__', '__copy__', '__deepcopy__', '__delattr__', '__doc__',
+'__eq__', '__getattribute__', '__hash__', '__init__', '__ne__',
+'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
+'__str__', '__unicode__', 'class', 'clone', 'createInjector',
+'equals', 'finalize', 'getClass', 'hashCode', 'notify', 'notifyAll',
+'registerNatives', 'toString', 'wait']
+----
+
+Startup script provides some convenient variables to access some global Gerrit components,
+for example a connection to the review database is kept open:
+
+----
+>>> ds
+org.apache.commons.dbcp.BasicDataSource@61db2215
+>>> ds.driverClassName
+u'org.postgresql.Driver'
+>>> ds.dataSource
+org.apache.commons.dbcp.PoolingDataSource@23226fe1
+>>> ds.dataSource.connection
+jdbc:postgresql://localhost/reviewdb, UserName=rv, PostgreSQL Native Driver
+----
+
+It is also possible to interact with the ORM layer:
+
+----
+>>> db = schk.schema.open()
+>>> db
+com.google.gerrit.reviewdb.server.ReviewDb_Schema_GwtOrm$$28@24cbbdf3
+>>> db.getDialect()
+com.google.gwtorm.schema.sql.DialectPostgreSQL@4de07d3e
+>>> for x in db.patchSets().iterateAllEntities():
+...     print x
+...
+[PatchSet 1,1]
+[PatchSet 2,1]
+[PatchSet 3,1]
+[PatchSet 4,1]
+[PatchSet 5,1]
+[PatchSet 6,1]
+[PatchSet 7,1]
+[PatchSet 8,1]
+[PatchSet 6,2]
+>>> for x in db.patchComments().iterateAllEntities():
+...     print x
+com.google.gerrit.reviewdb.client.PatchLineComment@5381298a
+com.google.gerrit.reviewdb.client.PatchLineComment@44ce4dda
+com.google.gerrit.reviewdb.client.PatchLineComment@44594680
+>>> dir(com.google.gerrit.reviewdb.client.PatchLineComment)
+['Key', 'STATUS_DRAFT', 'STATUS_PUBLISHED', 'Status', '__class__',
+'__copy__', '__deepcopy__', '__delattr__', '__doc__', '__eq__',
+'__getattribute__', '__hash__', '__init__', '__ne__', '__new__',
+'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__',
+'__unicode__', 'author', 'class', 'clone', 'equals', 'finalize',
+'getAuthor', 'getClass', 'getKey', 'getLine', 'getMessage',
+'getParentUuid', 'getSide', 'getStatus', 'getWrittenOn', 'hashCode',
+'key', 'line', 'lineNbr', 'message', 'notify', 'notifyAll',
+'parentUuid', 'registerNatives', 'setMessage', 'setSide', 'setStatus',
+'side', 'status', 'toString', 'updated', 'wait', 'writtenOn']
+>>> for x in db.patchComments().iterateAllEntities():
+...     print x.status, x.line, x.message
+...
+P 2 I like it!
+P 2 more
+P 1 better
+----
+
+A built-in *help()* function provides values of global variables
+defined in the interpreter:
+
+----
+>>> help()
+"schk" is "com.google.gerrit.server.schema.SchemaVersionCheck@5e8cb9bd"
+"ds" is "com.google.gerrit.server.schema.DataSourceProvider@6b3592c"
+"m" is "com.google.gerrit.lifecycle.LifecycleManager@6f03b248"
+"Shell" is "com.google.gerrit.pgm.shell.JythonShell@61644f2d"
+"d" is "com.google.gerrit.pgm.Daemon@28a3f689"
+
+Welcome to the Gerrit Inspector
+Enter help() to see the above again, EOF to quit and stop Gerrit
+----
+
+Java and Python exceptions are intercepted by the Inspector:
+----
+>>> import java.lang.RuntimeException
+>>> raise java.lang.RuntimeException("Exiting")
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
+        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
+        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
+        at java.lang.reflect.Constructor.newInstance(Constructor.java:532)
+        at org.python.core.PyReflectedConstructor.constructProxy(PyReflectedConstructor.java:210)
+
+java.lang.RuntimeException: java.lang.RuntimeException: Exiting
+>>>
+----
+
+To exit the interpreter, use EOF character (Ctrl-D on Unix systems, Ctrl-Z on Windows).
+
+It is also possible to shut down the JVM by using *System.exit()*
+
+----
+>>> import java.lang.System
+>>> java.lang.System.exit(1)
+----
+
+And Gerrit should shut down all its subsystems and exit:
+
+----
+[2012-04-17 15:31:08,458] INFO  com.google.gerrit.pgm.Daemon : caught shutdown, cleaning up
+----
+
+TROUBLESHOOTING
+---------------
+
+Gerrit Inspector is logging to the Gerrit error log.
+
+A successful startup is indicated in the logfile:
+
+----
+  [2012-04-17 13:43:44,888] INFO  com.google.gerrit.pgm.shell.JythonShell : Jython shell instance created.
+----
+
+If 'jython.jar' library is not available, Gerrit refuses to start when given *-s* option:
+
+----
+[2012-04-17 13:57:29,611] ERROR com.google.gerrit.pgm.Daemon : Unable to start daemon
+com.google.inject.ProvisionException: Guice provision errors:
+
+1) Error injecting constructor, java.lang.UnsupportedOperationException: Cannot create Jython shell: Class org.python.util.InteractiveConsole not found
+     (You might need to install jython.jar in the lib directory)
+  at com.google.gerrit.pgm.shell.JythonShell.<init>(JythonShell.java:47)
+  while locating com.google.gerrit.pgm.shell.JythonShell
+  while locating com.google.gerrit.pgm.shell.InteractiveShell
+----
+
+Errors during processing of the startup script, 'Startup.py', are logged
+to the error log:
+
+----
+[2012-04-17 14:20:30,558] INFO  com.google.gerrit.pgm.shell.JythonShell : Jython shell instance created.
+[2012-04-17 14:20:38,005] ERROR com.google.gerrit.pgm.shell.JythonShell : Exception occured while loading file Startup.py :
+java.lang.reflect.InvocationTargetException
+        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
+        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
+        at java.lang.reflect.Method.invoke(Method.java:616)
+        at com.google.gerrit.pgm.shell.JythonShell.runMethod0(JythonShell.java:112)
+        at com.google.gerrit.pgm.shell.JythonShell.execFile(JythonShell.java:194)
+        at com.google.gerrit.pgm.shell.JythonShell.reload(JythonShell.java:178)
+        at com.google.gerrit.pgm.shell.JythonShell.run(JythonShell.java:152)
+        at com.google.gerrit.pgm.Daemon.run(Daemon.java:190)
+        at com.google.gerrit.pgm.util.AbstractProgram.main(AbstractProgram.java:67)
+        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
+        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
+        at java.lang.reflect.Method.invoke(Method.java:616)
+        at com.google.gerrit.launcher.GerritLauncher.invokeProgram(GerritLauncher.java:167)
+        at com.google.gerrit.launcher.GerritLauncher.mainImpl(GerritLauncher.java:91)
+        at com.google.gerrit.launcher.GerritLauncher.main(GerritLauncher.java:49)
+        at Main.main(Main.java:25)
+Caused by: Traceback (most recent call last):
+  File "/home/user/.gerritcodereview/Startup.py", line 1, in <module>
+    Test
+NameError: name 'Test' is not defined
+----
+
+Those errors are non-fatal. System and user scripts can be loaded again
+by issuing the following command in the Gerrit Inspector console:
+
+----
+Shell.reload()
+----
+
+LOGGING
+-------
+Error and warning messages from the server are automatically written
+to the log file under '$site_path/logs/error_log'.
+
+Output and error messages (including Java and Python exceptions)
+resulting from interactive work are logged to the console.
+
+KNOWN ISSUES
+------------
+The Inspector does not yet recognize Google Guice bindings.
+
+IMPORTANT: Using the Inspector may void your warranty.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 717547b..d1c79ac 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -38,7 +38,7 @@
 ----
 mvn archetype:generate -DarchetypeGroupId=com.google.gerrit \
     -DarchetypeArtifactId=gerrit-plugin-archetype \
-    -DarchetypeVersion=2.8-SNAPSHOT \
+    -DarchetypeVersion=2.9-SNAPSHOT \
     -DgroupId=com.google.gerrit \
     -DartifactId=testPlugin
 ----
@@ -191,6 +191,29 @@
 }
 ----
 
+A plugin can get its canonical web URL injected at runtime:
+
+[source,java]
+----
+public class MyClass {
+
+  private final String url;
+
+  @Inject
+  public MyClass(@PluginCanonicalWebUrl String url) {
+    this.url = url;
+  }
+
+  [...]
+}
+----
+
+The URL is composed of the server's canonical web URL and the plugin's
+name, i.e. `http://review.example.com:8080/plugin-name`.
+
+The canonical web URL may be injected into any .jar plugin regardless of
+whether or not the plugin provides an HTTP servlet.
+
 [[reload_method]]
 Reload Method
 ~~~~~~~~~~~~~
@@ -364,7 +387,9 @@
 [source,java]
 ----
 import com.google.gerrit.sshd.SshCommand;
+import com.google.gerrit.sshd.CommandMetaData;
 
+@CommandMetaData(name="print", descr="Print hello command")
 class PrintHello extends SshCommand {
   protected abstract void run() {
     stdout.print("Hello\n");
@@ -398,7 +423,7 @@
 
 class MyCommands extends PluginCommandModule {
   protected void configureCommands() {
-    command("print").to(PrintHello.class);
+    command(PrintHello.class);
   }
 }
 ----
@@ -448,9 +473,33 @@
 $ ssh -p 29418 review.example.com shell ps
 ----
 
-[[configuration]]
-Configuration
--------------
+Single command plugins are also supported. In this scenario plugin binds
+SSH command to its own name. `SshModule` must inherit from
+`SingleCommandPluginModule` class:
+
+[source,java]
+----
+public class SshModule extends SingleCommandPluginModule {
+ @Override
+ protected void configure(LinkedBindingBuilder<Command> b) {
+    b.to(ShellCommand.class);
+  }
+}
+----
+
+If the plugin above is deployed under sh.jar file in `$site/plugins`
+directory, generic commands can be called without specifing the
+actual SSH command. Note in the example below, that the called commands
+`ls` and `ps` was not explicitly bound:
+
+----
+$ ssh -p 29418 review.example.com sh ls
+$ ssh -p 29418 review.example.com sh ps
+----
+
+[[simple-configuration]]
+Simple Configuration in `gerrit.config`
+---------------------------------------
 
 In Gerrit, global configuration is stored in the `gerrit.config` file.
 If a plugin needs global configuration, this configuration should be
@@ -460,12 +509,12 @@
 plugins that have a simple configuration that only consists of
 key-value pairs. With this approach it is not possible to have
 subsections in the plugin configuration. Plugins that require a complex
-configuration need to store their configuration in their own
-configuration file where they can make use of subsections. On the other
-hand storing the plugin configuration in a 'plugin' subsection in the
-`gerrit.config` file has the advantage that administrators have all
-configuration parameters in one file, instead of having one
-configuration file per plugin.
+configuration need to store their configuration in their
+link:#configuration[own configuration file] where they can make use of
+subsections. On the other hand storing the plugin configuration in a
+'plugin' subsection in the `gerrit.config` file has the advantage that
+administrators have all configuration parameters in one file, instead
+of having one configuration file per plugin.
 
 To avoid conflicts with other plugins, it is recommended that plugins
 only use the `plugin` subsection with their own name. For example the
@@ -492,9 +541,52 @@
                      .getString("language", "English");
 ----
 
-[[project-specific-configuration]]
-Project Specific Configuration
-------------------------------
+[[configuration]]
+Configuration in own config file
+--------------------------------
+
+Plugins can store their configuration in an own configuration file.
+This makes sense if the plugin configuration is rather complex and
+requires the usage of subsections. Plugins that have a simple
+key-value pair configuration can store their configuration in a
+link:#simple-configuration[`plugin` subsection of the `gerrit.config`
+file].
+
+The plugin configuration file must be named after the plugin and must
+be located in the `etc` folder of the review site. For example a
+configuration file for a `default-reviewer` plugin could look like
+this:
+
+.$site_path/etc/default-reviewer.config
+----
+[branch "refs/heads/master"]
+  reviewer = Project Owners
+  reviewer = john.doe@example.com
+[match "file:^.*\.txt"]
+  reviewer = My Info Developers
+----
+
+Via the `com.google.gerrit.server.config.PluginConfigFactory` class a
+plugin can easily access its configuration:
+
+[source,java]
+----
+@Inject
+private com.google.gerrit.server.config.PluginConfigFactory cfg;
+
+[...]
+
+String[] reviewers = cfg.getGlobalPluginConfig("default-reviewer")
+                        .getStringList("branch", "refs/heads/master", "reviewer");
+----
+
+The plugin configuration is loaded only once and is then cached.
+Similar to changes in 'gerrit.config', changes to the plugin
+configuration file will only become effective after a Gerrit restart.
+
+[[simple-project-specific-configuration]]
+Simple Project Specific Configuration in `project.config`
+---------------------------------------------------------
 
 In Gerrit, project specific configuration is stored in the project's
 `project.config` file on the `refs/meta/config` branch.  If a plugin
@@ -506,12 +598,12 @@
 plugins that have a simple configuration that only consists of
 key-value pairs. With this approach it is not possible to have
 subsections in the plugin configuration. Plugins that require a complex
-configuration need to store their configuration in their own
-configuration file where they can make use of subsections. On the other
-hand storing the plugin configuration in a 'plugin' subsection in the
-`project.config` file has the advantage that project owners have all
-configuration parameters in one file, instead of having one
-configuration file per plugin.
+configuration need to store their configuration in their
+link:#project-specific-configuration[own configuration file] where they
+can make use of subsections. On the other hand storing the plugin
+configuration in a 'plugin' subsection in the `project.config` file has
+the advantage that project owners have all configuration parameters in
+one file, instead of having one configuration file per plugin.
 
 To avoid conflicts with other plugins, it is recommended that plugins
 only use the `plugin` subsection with their own name. For example the
@@ -556,6 +648,63 @@
 `refs/meta/config` branch, editing the `project.config` file and
 pushing the commit back.
 
+[[project-specific-configuration]]
+Project Specific Configuration in own config file
+-------------------------------------------------
+
+Plugins can store their project specific configuration in an own
+configuration file in the projects `refs/meta/config` branch.
+This makes sense if the plugins project specific configuration is
+rather complex and requires the usage of subsections. Plugins that
+have a simple key-value pair configuration can store their project
+specific configuration in a link:#simple-project-specific-configuration[
+`plugin` subsection of the `project.config` file].
+
+The plugin configuration file in the `refs/meta/config` branch must be
+named after the plugin. For example a configuration file for a
+`default-reviewer` plugin could look like this:
+
+.default-reviewer.config
+----
+[branch "refs/heads/master"]
+  reviewer = Project Owners
+  reviewer = john.doe@example.com
+[match "file:^.*\.txt"]
+  reviewer = My Info Developers
+----
+
+Via the `com.google.gerrit.server.config.PluginConfigFactory` class a
+plugin can easily access its project specific configuration:
+
+[source,java]
+----
+@Inject
+private com.google.gerrit.server.config.PluginConfigFactory cfg;
+
+[...]
+
+String[] reviewers = cfg.getProjectPluginConfig(project, "default-reviewer")
+                        .getStringList("branch", "refs/heads/master", "reviewer");
+----
+
+It is also possible to get missing configuration parameters inherited
+from the parent projects:
+
+[source,java]
+----
+@Inject
+private com.google.gerrit.server.config.PluginConfigFactory cfg;
+
+[...]
+
+String[] reviewers = cfg.getFromPluginConfigWithInheritance(project, "default-reviewer")
+                        .getStringList("branch", "refs/heads/master", "reviewer");
+----
+
+Project owners can edit the project configuration by fetching the
+`refs/meta/config` branch, editing the `<plugin-name>.config` file and
+pushing the commit back.
+
 [[capabilities]]
 Plugin Owned Capabilities
 -------------------------
@@ -988,6 +1137,303 @@
 Gerrit-Module: com.googlesource.gerrit.plugins.helloworld.HelloWorldModule
 ----
 
+It is also possible to show some menu entries only if the user has a
+certain capability:
+
+[source,java]
+----
+public class MyTopMenuExtension implements TopMenu {
+  private final String pluginName;
+  private final Provider<CurrentUser> userProvider;
+  private final List<MenuEntry> menuEntries;
+
+  @Inject
+  public MyTopMenuExtension(@PluginName String pluginName,
+      Provider<CurrentUser> userProvider) {
+    this.pluginName = pluginName;
+    this.userProvider = userProvider;
+    menuEntries = new ArrayList<TopMenu.MenuEntry>();
+
+    // add menu entry that is only visible to users with a certain capability
+    if (canSeeMenuEntry()) {
+      menuEntries.add(new MenuEntry("Top Menu Entry", Collections
+          .singletonList(new MenuItem("Gerrit", "http://gerrit.googlecode.com/"))));
+    }
+
+    // add menu entry that is visible to all users (even anonymous users)
+    menuEntries.add(new MenuEntry("Top Menu Entry", Collections
+          .singletonList(new MenuItem("Documentation", "/plugins/myplugin/"))));
+  }
+
+  private boolean canSeeMenuEntry() {
+    if (userProvider.get().isIdentifiedUser()) {
+      CapabilityControl ctl = userProvider.get().getCapabilities();
+      return ctl.canPerform(pluginName + "-" + MyCapability.ID)
+          || ctl.canAdministrateServer();
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public List<MenuEntry> getEntries() {
+    return menuEntries;
+  }
+}
+----
+
+[[gwt_ui_extension]]
+GWT UI Extension
+----------------
+Plugins can extend the Gerrit UI with own GWT code.
+
+The Maven archetype 'gerrit-plugin-gwt-archetype' can be used to
+generate a GWT plugin skeleton. How to use the Maven plugin archetypes
+is described in the link:#getting-started[Getting started] section.
+
+The generated GWT plugin has a link:#top-menu-extensions[top menu] that
+opens a GWT dialog box when the user clicks on it.
+
+In addition to the Gerrit-Plugin API a GWT plugin depends on
+`gerrit-plugin-gwtui`. This dependency must be specified in the
+`pom.xml`:
+
+[source,xml]
+----
+<dependency>
+  <groupId>com.google.gerrit</groupId>
+  <artifactId>gerrit-plugin-gwtui</artifactId>
+  <version>${Gerrit-ApiVersion}</version>
+</dependency>
+----
+
+A GWT plugin must contain a GWT module file, e.g. `HelloPlugin.gwt.xml`,
+that bundles together all the configuration settings of the GWT plugin:
+
+[source,xml]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<module rename-to="hello_gwt_plugin">
+  <!-- Inherit the core Web Toolkit stuff. -->
+  <inherits name="com.google.gwt.user.User"/>
+  <!-- Other module inherits -->
+  <inherits name="com.google.gerrit.Plugin"/>
+  <inherits name="com.google.gwt.http.HTTP"/>
+  <!-- Using GWT built-in themes adds a number of static -->
+  <!-- resources to the plugin. No theme inherits lines were -->
+  <!-- added in order to make this plugin as simple as possible -->
+  <!-- Specify the app entry point class. -->
+  <entry-point class="${package}.client.HelloPlugin"/>
+  <stylesheet src="hello.css"/>
+</module>
+----
+
+The GWT module must inherit `com.google.gerrit.Plugin` and
+`com.google.gwt.http.HTTP`.
+
+To register the GWT module a `GwtPlugin` needs to be bound.
+
+If no Guice modules are declared in the manifest, the GWT plugin may
+use auto-registration by using the `@Listen` annotation:
+
+[source,java]
+----
+@Listen
+public class MyExtension extends GwtPlugin {
+  public MyExtension() {
+    super("hello_gwt_plugin");
+  }
+}
+----
+
+Otherwise the binding must be done in an `HttpModule`:
+
+[source,java]
+----
+public class HttpModule extends HttpPluginModule {
+
+  @Override
+  protected void configureServlets() {
+    DynamicSet.bind(binder(), WebUiPlugin.class)
+        .toInstance(new GwtPlugin("hello_gwt_plugin"));
+  }
+}
+----
+
+The HTTP module above must be declared in the `pom.xml` for Maven
+driven plugins:
+
+[source,xml]
+----
+<manifestEntries>
+  <Gerrit-HttpModule>com.googlesource.gerrit.plugins.myplugin.HttpModule</Gerrit-HttpModule>
+</manifestEntries>
+----
+
+It is important that the module name that is provided to the
+`GwtPlugin` matches the GWT module contained in the plugin. The name
+of the GWT module can be explicitly set in the GWT module file by
+specifying the `rename-to` attribute on the module.
+
+[source,xml]
+----
+<module rename-to="hello_gwt_plugin">
+----
+
+The actual GWT code must be implemented in a class that extends
+`com.google.gerrit.plugin.client.Plugin`:
+
+[source,java]
+----
+public class HelloPlugin extends Plugin {
+
+  @Override
+  public void onModuleLoad() {
+    // Create the dialog box
+    final DialogBox dialogBox = new DialogBox();
+
+    // The content of the dialog comes from a User specified Preference
+    dialogBox.setText("Hello from GWT Gerrit UI plugin");
+    dialogBox.setAnimationEnabled(true);
+    Button closeButton = new Button("Close");
+    VerticalPanel dialogVPanel = new VerticalPanel();
+    dialogVPanel.setWidth("100%");
+    dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER);
+    dialogVPanel.add(closeButton);
+
+    closeButton.addClickHandler(new ClickHandler() {
+      public void onClick(ClickEvent event) {
+        dialogBox.hide();
+      }
+    });
+
+    // Set the contents of the Widget
+    dialogBox.setWidget(dialogVPanel);
+
+    RootPanel rootPanel = RootPanel.get(HelloMenu.MENU_ID);
+    rootPanel.getElement().removeAttribute("href");
+    rootPanel.addDomHandler(new ClickHandler() {
+        @Override
+        public void onClick(ClickEvent event) {
+          dialogBox.center();
+          dialogBox.show();
+        }
+    }, ClickEvent.getType());
+  }
+}
+----
+
+This class must be set as entry point in the GWT module:
+
+[source,xml]
+----
+<entry-point class="${package}.client.HelloPlugin"/>
+----
+
+In addition this class must be defined as module in the `pom.xml` for the
+`gwt-maven-plugin` and the `webappDirectory` option of `gwt-maven-plugin`
+must be set to `${project.build.directory}/classes/static`:
+
+[source,xml]
+----
+<plugin>
+  <groupId>org.codehaus.mojo</groupId>
+  <artifactId>gwt-maven-plugin</artifactId>
+  <version>2.5.1</version>
+  <configuration>
+    <module>com.googlesource.gerrit.plugins.myplugin.HelloPlugin</module>
+    <disableClassMetadata>true</disableClassMetadata>
+    <disableCastChecking>true</disableCastChecking>
+    <webappDirectory>${project.build.directory}/classes/static</webappDirectory>
+  </configuration>
+  <executions>
+    <execution>
+      <goals>
+        <goal>compile</goal>
+      </goals>
+    </execution>
+  </executions>
+</plugin>
+----
+
+To attach a GWT widget defined by the plugin to the Gerrit core UI
+`com.google.gwt.user.client.ui.RootPanel` can be used to manipulate the
+Gerrit core widgets:
+
+[source,java]
+----
+RootPanel rootPanel = RootPanel.get(HelloMenu.MENU_ID);
+rootPanel.getElement().removeAttribute("href");
+rootPanel.addDomHandler(new ClickHandler() {
+  @Override
+  public void onClick(ClickEvent event) {
+    dialogBox.center();
+    dialogBox.show();
+  }
+}, ClickEvent.getType());
+----
+
+GWT plugins can come with their own css file. This css file must have a
+unique name and must be registered in the GWT module:
+
+[source,xml]
+----
+<stylesheet src="hello.css"/>
+----
+
+If a GWT plugin wants to invoke the Gerrit REST API it can use
+`com.google.gerrit.plugin.client.rpc.RestApi` to contruct the URL
+path and to trigger the REST calls.
+
+Example for invoking a Gerrit core REST endpoint:
+
+[source,java]
+----
+new RestApi("projects").id(projectName).view("description")
+    .put("new description", new AsyncCallback<JavaScriptObject>() {
+
+  @Override
+  public void onSuccess(JavaScriptObject result) {
+    // TODO
+  }
+
+  @Override
+  public void onFailure(Throwable caught) {
+    // never invoked
+  }
+});
+----
+
+Example for invoking a REST endpoint defined by a plugin:
+
+[source,java]
+----
+new RestApi("projects").id(projectName).view("myplugin", "myview")
+    .get(new AsyncCallback<JavaScriptObject>() {
+
+  @Override
+  public void onSuccess(JavaScriptObject result) {
+    // TODO
+  }
+
+  @Override
+  public void onFailure(Throwable caught) {
+    // never invoked
+  }
+});
+----
+
+The `onFailure(Throwable)` of the provided callback is never invoked.
+If an error occurs, it is shown in an error dialog.
+
+In order to be able to do REST calls the GWT module must inherit
+`com.google.gwt.json.JSON`:
+
+[source,xml]
+----
+<inherits name="com.google.gwt.json.JSON"/>
+----
+
 [[http]]
 HTTP Servlets
 -------------
@@ -1197,3 +1643,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-readme.txt b/Documentation/dev-readme.txt
index ced8648..d961f70 100644
--- a/Documentation/dev-readme.txt
+++ b/Documentation/dev-readme.txt
@@ -106,6 +106,43 @@
   java -jar buck-out/gen/gerrit.war daemon -d ../test_site
 ----
 
+Running the Daemon with Gerrit Inspector
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+link:dev-inspector.html[Gerrit Inspector] is an interactive scriptable
+environment to inspect and modify internal state of the system.
+
+This environment is available on the system console after
+the system starts. Leaving the Inspector will shutdown the Gerrit
+instance.
+
+The environment allows interactive work as well as running of
+Python scripts for troubleshooting.
+
+Gerrit Inspect can be started by adding '-s' option to the
+command used to launch the daemon:
+
+----
+  java -jar buck-out/gen/gerrit.war daemon -d ../test_site -s
+----
+
+Gerrit Inspector examines Java libraries first, then loads
+its initialization scripts and then starts a command line
+prompt on the console:
+
+----
+  Welcome to the Gerrit Inspector
+  Enter help() to see the above again, EOF to quit and stop Gerrit
+  Jython 2.5.2 (Release_2_5_2:7206, Mar 2 2011, 23:12:06)
+  [OpenJDK 64-Bit Server VM (Sun Microsystems Inc.)] on java1.6.0 running for Gerrit 2.3-rc0-163-g01967ef
+  >>>
+----
+
+With the Inspector enabled Gerrit can be used normally and all
+interfaces (HTTP, SSH etc.) are available.
+
+Care must be taken not to modify internal state of the system
+when using the Inspector.
 
 Querying the Database
 ~~~~~~~~~~~~~~~~~~~~~
@@ -209,3 +246,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-release-deploy-config.txt b/Documentation/dev-release-deploy-config.txt
index 9c98fff..6ea32b7 100644
--- a/Documentation/dev-release-deploy-config.txt
+++ b/Documentation/dev-release-deploy-config.txt
@@ -132,3 +132,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-release-subproject.txt b/Documentation/dev-release-subproject.txt
index 956bd29..b9a39be 100644
--- a/Documentation/dev-release-subproject.txt
+++ b/Documentation/dev-release-subproject.txt
@@ -107,3 +107,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt
index f9d0d0e..b9b9ff0 100644
--- a/Documentation/dev-release.txt
+++ b/Documentation/dev-release.txt
@@ -359,3 +359,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-rest-api.txt b/Documentation/dev-rest-api.txt
index 0175a62..24b60f0 100644
--- a/Documentation/dev-rest-api.txt
+++ b/Documentation/dev-rest-api.txt
@@ -87,3 +87,5 @@
 ------
 Part of link:index.html[Gerrit Code Review]
 
+SEARCHBOX
+---------
diff --git a/Documentation/error-branch-not-found.txt b/Documentation/error-branch-not-found.txt
index bd8d090..a8b1741 100644
--- a/Documentation/error-branch-not-found.txt
+++ b/Documentation/error-branch-not-found.txt
@@ -32,3 +32,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-change-closed.txt b/Documentation/error-change-closed.txt
index 3244fb3..e466c2e 100644
--- a/Documentation/error-change-closed.txt
+++ b/Documentation/error-change-closed.txt
@@ -39,3 +39,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-change-does-not-belong-to-project.txt b/Documentation/error-change-does-not-belong-to-project.txt
index e747881..db16261 100644
--- a/Documentation/error-change-does-not-belong-to-project.txt
+++ b/Documentation/error-change-does-not-belong-to-project.txt
@@ -14,3 +14,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-change-not-found.txt b/Documentation/error-change-not-found.txt
index b6df13b..e578ef5 100644
--- a/Documentation/error-change-not-found.txt
+++ b/Documentation/error-change-not-found.txt
@@ -13,3 +13,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-change-upload-blocked.txt b/Documentation/error-change-upload-blocked.txt
index 6bad02e..3b413bd2 100644
--- a/Documentation/error-change-upload-blocked.txt
+++ b/Documentation/error-change-upload-blocked.txt
@@ -39,3 +39,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-commit-already-exists.txt b/Documentation/error-commit-already-exists.txt
index dc32c4c1..6a2170a 100644
--- a/Documentation/error-commit-already-exists.txt
+++ b/Documentation/error-commit-already-exists.txt
@@ -12,3 +12,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-contains-banned-commit.txt b/Documentation/error-contains-banned-commit.txt
index 8a30c44..f6ac30d 100644
--- a/Documentation/error-contains-banned-commit.txt
+++ b/Documentation/error-contains-banned-commit.txt
@@ -19,3 +19,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-has-duplicates.txt b/Documentation/error-has-duplicates.txt
index b5175c0..4eac779 100644
--- a/Documentation/error-has-duplicates.txt
+++ b/Documentation/error-has-duplicates.txt
@@ -22,3 +22,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-invalid-author.txt b/Documentation/error-invalid-author.txt
index c484776..7067cde 100644
--- a/Documentation/error-invalid-author.txt
+++ b/Documentation/error-invalid-author.txt
@@ -143,3 +143,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-invalid-changeid-line.txt b/Documentation/error-invalid-changeid-line.txt
index 9235266..a1d7f15 100644
--- a/Documentation/error-invalid-changeid-line.txt
+++ b/Documentation/error-invalid-changeid-line.txt
@@ -29,3 +29,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-invalid-committer.txt b/Documentation/error-invalid-committer.txt
index 447064e..e27961f 100644
--- a/Documentation/error-invalid-committer.txt
+++ b/Documentation/error-invalid-committer.txt
@@ -108,3 +108,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-messages.txt b/Documentation/error-messages.txt
index 58e9e02..18aa12a 100644
--- a/Documentation/error-messages.txt
+++ b/Documentation/error-messages.txt
@@ -47,3 +47,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-missing-changeid.txt b/Documentation/error-missing-changeid.txt
index edbc63b..833e6ac 100644
--- a/Documentation/error-missing-changeid.txt
+++ b/Documentation/error-missing-changeid.txt
@@ -70,3 +70,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-multiple-changeid-lines.txt b/Documentation/error-multiple-changeid-lines.txt
index 9fa2b91..9b094d4 100644
--- a/Documentation/error-multiple-changeid-lines.txt
+++ b/Documentation/error-multiple-changeid-lines.txt
@@ -29,3 +29,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-no-changes-made.txt b/Documentation/error-no-changes-made.txt
index d0e1d4f..4987b16 100644
--- a/Documentation/error-no-changes-made.txt
+++ b/Documentation/error-no-changes-made.txt
@@ -19,3 +19,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-no-common-ancestry.txt b/Documentation/error-no-common-ancestry.txt
index 615da71..174e52b 100644
--- a/Documentation/error-no-common-ancestry.txt
+++ b/Documentation/error-no-common-ancestry.txt
@@ -18,3 +18,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-no-new-changes.txt b/Documentation/error-no-new-changes.txt
index 8e409ef..20357a4 100644
--- a/Documentation/error-no-new-changes.txt
+++ b/Documentation/error-no-new-changes.txt
@@ -49,3 +49,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-non-fast-forward.txt b/Documentation/error-non-fast-forward.txt
index 6604e10..f9b2caa 100644
--- a/Documentation/error-non-fast-forward.txt
+++ b/Documentation/error-non-fast-forward.txt
@@ -57,3 +57,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-not-a-gerrit-administrator.txt b/Documentation/error-not-a-gerrit-administrator.txt
index b771af6..98dd64c 100644
--- a/Documentation/error-not-a-gerrit-administrator.txt
+++ b/Documentation/error-not-a-gerrit-administrator.txt
@@ -12,3 +12,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-not-allowed-to-upload-merges.txt b/Documentation/error-not-allowed-to-upload-merges.txt
index 515eef5..fd2f10c 100644
--- a/Documentation/error-not-allowed-to-upload-merges.txt
+++ b/Documentation/error-not-allowed-to-upload-merges.txt
@@ -19,3 +19,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-not-permitted-to-create.txt b/Documentation/error-not-permitted-to-create.txt
index 9c07fd1..f41b645 100644
--- a/Documentation/error-not-permitted-to-create.txt
+++ b/Documentation/error-not-permitted-to-create.txt
@@ -14,3 +14,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-not-signed-off-by.txt b/Documentation/error-not-signed-off-by.txt
index bd1f40d..a3d6bbd 100644
--- a/Documentation/error-not-signed-off-by.txt
+++ b/Documentation/error-not-signed-off-by.txt
@@ -29,3 +29,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-not-valid-ref.txt b/Documentation/error-not-valid-ref.txt
index 128e796..5bba8e6 100644
--- a/Documentation/error-not-valid-ref.txt
+++ b/Documentation/error-not-valid-ref.txt
@@ -44,3 +44,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-permission-denied.txt b/Documentation/error-permission-denied.txt
index 2ec0a3f..a97161f 100644
--- a/Documentation/error-permission-denied.txt
+++ b/Documentation/error-permission-denied.txt
@@ -60,3 +60,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-prohibited-by-gerrit.txt b/Documentation/error-prohibited-by-gerrit.txt
index bad2b3c..2a65ea2 100644
--- a/Documentation/error-prohibited-by-gerrit.txt
+++ b/Documentation/error-prohibited-by-gerrit.txt
@@ -39,3 +39,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-project-not-found.txt b/Documentation/error-project-not-found.txt
index 3fc0141..29af5f8 100644
--- a/Documentation/error-project-not-found.txt
+++ b/Documentation/error-project-not-found.txt
@@ -32,3 +32,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-push-fails-due-to-commit-message.txt b/Documentation/error-push-fails-due-to-commit-message.txt
index 172d64f..046789e 100644
--- a/Documentation/error-push-fails-due-to-commit-message.txt
+++ b/Documentation/error-push-fails-due-to-commit-message.txt
@@ -39,3 +39,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-squash-commits-first.txt b/Documentation/error-squash-commits-first.txt
index 2181c52..9434363 100644
--- a/Documentation/error-squash-commits-first.txt
+++ b/Documentation/error-squash-commits-first.txt
@@ -106,3 +106,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-upload-denied.txt b/Documentation/error-upload-denied.txt
index 5dec8ab..6de94b4 100644
--- a/Documentation/error-upload-denied.txt
+++ b/Documentation/error-upload-denied.txt
@@ -17,3 +17,6 @@
 GERRIT
 ------
 Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/i18n-readme.txt b/Documentation/i18n-readme.txt
index 2135598..46abaad 100644
--- a/Documentation/i18n-readme.txt
+++ b/Documentation/i18n-readme.txt
@@ -22,3 +22,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/images/intro-quick-central-gerrit.png b/Documentation/images/intro-quick-central-gerrit.png
index 61b9638..8717176 100644
--- a/Documentation/images/intro-quick-central-gerrit.png
+++ b/Documentation/images/intro-quick-central-gerrit.png
Binary files differ
diff --git a/Documentation/images/intro-quick-central-repo.png b/Documentation/images/intro-quick-central-repo.png
index 84ffeb0..8400b5e 100644
--- a/Documentation/images/intro-quick-central-repo.png
+++ b/Documentation/images/intro-quick-central-repo.png
Binary files differ
diff --git a/Documentation/index.txt b/Documentation/index.txt
index 58dae6e..e696bfb 100644
--- a/Documentation/index.txt
+++ b/Documentation/index.txt
@@ -91,3 +91,6 @@
 * link:http://code.google.com/p/gerrit/issues/list[Issue Tracking]
 * link:http://code.google.com/p/gerrit/source/checkout[Source Code]
 * link:http://code.google.com/p/gerrit/wiki/Background[A History of Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/install-j2ee.txt b/Documentation/install-j2ee.txt
index 5ba8cb1..dd9657c 100644
--- a/Documentation/install-j2ee.txt
+++ b/Documentation/install-j2ee.txt
@@ -117,3 +117,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/install-quick.txt b/Documentation/install-quick.txt
index f1bd25c..741de35 100644
--- a/Documentation/install-quick.txt
+++ b/Documentation/install-quick.txt
@@ -232,3 +232,6 @@
 ------
 
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/install.txt b/Documentation/install.txt
index 1d6d1bd..8e836f8 100644
--- a/Documentation/install.txt
+++ b/Documentation/install.txt
@@ -194,3 +194,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/intro-change-screen.txt b/Documentation/intro-change-screen.txt
index 2913336..80ac974 100644
--- a/Documentation/intro-change-screen.txt
+++ b/Documentation/intro-change-screen.txt
@@ -28,10 +28,10 @@
 Configuration
 -------------
 
-The new change screen is deactivated by default. It can be activated system-wide
-by changing the link:config-gerrit.html[gerrit.changeScreen] setting to
-`CHANGE_SCREEN2`.  Users can deactivate it by setting `OLD_UI` on their user
-preferences page.
+The new change screen is activated by default. It can be deactivated
+system-wide by changing the link:config-gerrit.html[gerrit.changeScreen]
+setting to `OLD_UI`.  Users can deactivate it by setting `OLD_UI` on their
+user preferences page.
 
 [[switching-between-patch-sets]]
 Switching between patch sets
@@ -214,3 +214,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/intro-quick.txt b/Documentation/intro-quick.txt
index ae2e7e7..cd3374b 100644
--- a/Documentation/intro-quick.txt
+++ b/Documentation/intro-quick.txt
@@ -390,3 +390,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/js-api.txt b/Documentation/js-api.txt
index 2e97ac4..7411026 100644
--- a/Documentation/js-api.txt
+++ b/Documentation/js-api.txt
@@ -425,6 +425,13 @@
   comes with an onkeypress handler installed to play nicely with
   Gerrit's keyboard binding system.
 
+* `select(a,i)`: a new `<select>` element containing one `<option>`
+  element for each entry in the provided array `a`.  The option with
+  the index `i` will be pre-selected in the drop-down-list.
+
+* `selected(s)`: returns the text of the `<option>` element that is
+  currently selected in the provided `<select>` element `s`.
+
 * `span(...)`: a new `<span>` wrapping the (optional) arguments.
 
 * `msg(label)`: a new label.
@@ -636,3 +643,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/json.txt b/Documentation/json.txt
index 2836e5b..73dbd92 100644
--- a/Documentation/json.txt
+++ b/Documentation/json.txt
@@ -72,6 +72,9 @@
 submitRecords:: The <<submitRecord,submitRecord attribute>> contains
 information about whether this change has been or can be submitted.
 
+allReviewers:: List of all reviewers in <<account,account attribute>>
+which are added to a change.
+
 [[trackingid]]
 trackingid
 ----------
@@ -273,3 +276,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/pgm-LocalUsernamesToLowerCase.txt b/Documentation/pgm-LocalUsernamesToLowerCase.txt
index 9189fee..f1d21f89 100644
--- a/Documentation/pgm-LocalUsernamesToLowerCase.txt
+++ b/Documentation/pgm-LocalUsernamesToLowerCase.txt
@@ -66,3 +66,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/pgm-ScanTrackingIds.txt b/Documentation/pgm-ScanTrackingIds.txt
index f9494d4..9e38032 100644
--- a/Documentation/pgm-ScanTrackingIds.txt
+++ b/Documentation/pgm-ScanTrackingIds.txt
@@ -57,3 +57,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/pgm-daemon.txt b/Documentation/pgm-daemon.txt
index ce88ab7..18bd293 100644
--- a/Documentation/pgm-daemon.txt
+++ b/Documentation/pgm-daemon.txt
@@ -16,6 +16,7 @@
 	[\--slave]
 	[\--headless]
 	[\--init]
+	[-s]
 
 DESCRIPTION
 -----------
@@ -68,6 +69,14 @@
 	Run init before starting the daemon. This will create a new site or
 	upgrade an existing site.
 
+\--s::
+	Start link:dev-inspector.html[Gerrit Inspector] on the console, a
+	built-in interactive inspection environment to assist debugging and
+	troubleshooting of Gerrit code.
++
+This options requires 'jython.jar' from the http://www.jython.org[Jython distribution]
+to be present in '$site_path/lib' directory.
+
 CONTEXT
 -------
 This command can only be run on a server which has direct
@@ -117,3 +126,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/pgm-gsql.txt b/Documentation/pgm-gsql.txt
index 37fbb74..a805036 100644
--- a/Documentation/pgm-gsql.txt
+++ b/Documentation/pgm-gsql.txt
@@ -54,3 +54,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/pgm-index.txt b/Documentation/pgm-index.txt
index 987b4ac..a25a1ab 100644
--- a/Documentation/pgm-index.txt
+++ b/Documentation/pgm-index.txt
@@ -44,3 +44,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/pgm-init.txt b/Documentation/pgm-init.txt
index 3d6cb73..8e48b30 100644
--- a/Documentation/pgm-init.txt
+++ b/Documentation/pgm-init.txt
@@ -62,3 +62,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/pgm-prolog-shell.txt b/Documentation/pgm-prolog-shell.txt
index 3189e90..7b273f5 100644
--- a/Documentation/pgm-prolog-shell.txt
+++ b/Documentation/pgm-prolog-shell.txt
@@ -55,3 +55,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/pgm-reindex.txt b/Documentation/pgm-reindex.txt
index 2b44f6b..f55c093 100644
--- a/Documentation/pgm-reindex.txt
+++ b/Documentation/pgm-reindex.txt
@@ -39,3 +39,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/pgm-rulec.txt b/Documentation/pgm-rulec.txt
index 6d0a632..356f555 100644
--- a/Documentation/pgm-rulec.txt
+++ b/Documentation/pgm-rulec.txt
@@ -52,3 +52,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/project-setup.txt b/Documentation/project-setup.txt
index 36d3c60..9b6f6d9 100644
--- a/Documentation/project-setup.txt
+++ b/Documentation/project-setup.txt
@@ -134,3 +134,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/prolog-change-facts.txt b/Documentation/prolog-change-facts.txt
index e21da02..071984b 100644
--- a/Documentation/prolog-change-facts.txt
+++ b/Documentation/prolog-change-facts.txt
@@ -101,3 +101,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/prolog-cookbook.txt b/Documentation/prolog-cookbook.txt
index b1710a2..54f6910 100644
--- a/Documentation/prolog-cookbook.txt
+++ b/Documentation/prolog-cookbook.txt
@@ -1062,3 +1062,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/replace_macros.py b/Documentation/replace_macros.py
index 1680a47..1f23cbe 100755
--- a/Documentation/replace_macros.py
+++ b/Documentation/replace_macros.py
@@ -22,6 +22,7 @@
 PAT_GET = re.compile(r'^get::([^ \t\n]*)')
 PAT_TITLE = re.compile(r'^\.(.*)')
 PAT_STARS = re.compile(r'^\*\*\*\*')
+PAT_SEARCHBOX = re.compile(r'^SEARCHBOX')
 
 GERRIT_UPLINK = """
 
@@ -56,6 +57,29 @@
 
 """
 
+SEARCH_BOX = """
+
+++++
+<div style="position:absolute; right:20px; top:20px;">
+<input type="text" id="docSearch" size="70" />
+<button type="button" id="searchBox">Search</button>
+<script type="text/javascript">
+var f = function() {
+  window.location = '../#/Documentation/' +
+    encodeURIComponent(document.getElementById("docSearch").value);
+}
+document.getElementById("searchBox").onclick = f;
+document.getElementById("docSearch").onkeypress = function(e) {
+  if (13 == (e.keyCode ? e.keyCode : e.which)) {
+    f();
+  }
+}
+</script>
+</div>
+++++
+
+"""
+
 opts = OptionParser()
 opts.add_option('-o', '--out', help='output file')
 opts.add_option('-s', '--src', help='source file')
@@ -73,6 +97,10 @@
       # Case of "GERRIT\n------" at the footer
       out_file.write(GERRIT_UPLINK)
       last_line = ''
+    elif PAT_SEARCHBOX.match(line):
+      # Case of 'SEARCHBOX\n---------'
+      out_file.write(SEARCH_BOX)
+      last_line = ''
     elif PAT_INCLUDE.match(line):
       # Case of 'include::<filename>'
       match = PAT_INCLUDE.match(line)
diff --git a/Documentation/rest-api-access.txt b/Documentation/rest-api-access.txt
index 6c786e4..b49f787 100644
--- a/Documentation/rest-api-access.txt
+++ b/Documentation/rest-api-access.txt
@@ -378,3 +378,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 70431c6..f72c154 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -200,14 +200,17 @@
   GET /accounts/john.doe@example.com/active HTTP/1.0
 ----
 
-As response `200 OK` is returned for an active account and
-`404 Not Found` is returned for an inactive account.
+If the account is active the string `ok` is returned.
 
 .Response
 ----
   HTTP/1.1 200 OK
+
+  ok
 ----
 
+If the account is inactive the response is `204 No Content`.
+
 [[set-active]]
 Set Active
 ~~~~~~~~~~
@@ -1331,3 +1334,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 9404d00..9ed8be0 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -3371,3 +3371,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index 5bd5e0c..9a512b8 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -220,3 +220,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/rest-api-documentation.txt b/Documentation/rest-api-documentation.txt
new file mode 100644
index 0000000..9030fac
--- /dev/null
+++ b/Documentation/rest-api-documentation.txt
@@ -0,0 +1,154 @@
+Gerrit Code Review - /Documentation/ REST API
+=============================================
+
+This page describes the documentation search related REST endpoints.
+Please also take note of the general information on the
+link:rest-api.html[REST API].
+
+Please note that this feature is only usable with documentation built-in.
+You'll need to
+`buck build :withdocs`
+or
+`buck build :release`
+to test this feature.
+
+[[documentation-endpoints]]
+Documentation Search Endpoints
+------------------------------
+
+[[search-documentation]]
+Search Documentation
+~~~~~~~~~~~~~~~~~~~~
+[verse]
+'GET /Documentation/'
+
+With `q` parameter, search our documentation index for the terms.
+
+A list of link:#doc-result[DocResult] entities is returned describing the
+results.
+
+.Request
+----
+  GET /Documentation/?q=test HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  [
+    {
+      "title": "Gerrit Code Review - REST API Developers\u0027 Notes",
+      "url": "Documentation/dev-rest-api.html"
+    },
+    {
+      "title": "Gerrit Code Review - REST API",
+      "url": "Documentation/rest-api.html"
+    },
+    {
+      "title": "Gerrit Code Review - JavaScript API",
+      "url": "Documentation/js-api.html"
+    },
+    {
+      "title": "Gerrit Code Review - /plugins/ REST API",
+      "url": "Documentation/rest-api-plugins.html"
+    },
+    {
+      "title": "Gerrit Code Review - /config/ REST API",
+      "url": "Documentation/rest-api-config.html"
+    },
+    {
+      "title": "Gerrit Code Review for Git",
+      "url": "Documentation/index.html"
+    },
+    {
+      "title": "Gerrit Code Review - /access/ REST API",
+      "url": "Documentation/rest-api-access.html"
+    },
+    {
+      "title": "Gerrit Code Review - Plugin Development",
+      "url": "Documentation/dev-plugins.html"
+    },
+    {
+      "title": "Gerrit Code Review - Developer Setup",
+      "url": "Documentation/dev-readme.html"
+    },
+    {
+      "title": "Gerrit Code Review - Hooks",
+      "url": "Documentation/config-hooks.html"
+    },
+    {
+      "title": "Change Screen - Introduction",
+      "url": "Documentation/intro-change-screen.html"
+    },
+    {
+      "title": "Gerrit Code Review - /groups/ REST API",
+      "url": "Documentation/rest-api-groups.html"
+    },
+    {
+      "title": "Gerrit Code Review - /accounts/ REST API",
+      "url": "Documentation/rest-api-accounts.html"
+    },
+    {
+      "title": "Gerrit Code Review - /projects/ REST API",
+      "url": "Documentation/rest-api-documentation.html"
+    },
+    {
+      "title": "Gerrit Code Review - /projects/ REST API",
+      "url": "Documentation/rest-api-projects.html"
+    },
+    {
+      "title": "Gerrit Code Review - Prolog Submit Rules Cookbook",
+      "url": "Documentation/prolog-cookbook.html"
+    },
+    {
+      "title": "Gerrit Code Review - /changes/ REST API",
+      "url": "Documentation/rest-api-changes.html"
+    },
+    {
+      "title": "Gerrit Code Review - Configuration",
+      "url": "Documentation/config-gerrit.html"
+    },
+    {
+      "title": "Gerrit Code Review - Access Controls",
+      "url": "Documentation/access-control.html"
+    },
+    {
+      "title": "Gerrit Code Review - Licenses",
+      "url": "Documentation/licenses.html"
+    }
+  ]
+----
+
+.Query documentation
+****
+get::/Documentation/?q=keyword
+****
+
+
+[[json-entities]]
+JSON Entities
+-------------
+
+[[doc-result]]
+DocResult
+~~~~~~~~~
+The `DocResult` entity contains information about a document.
+
+[options="header",width="50%",cols="1,^2,4"]
+|=========================
+|Field Name  ||Description
+|`title`     ||The title of the document.
+|`url`       ||The URL of the document.
+|=========================
+
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/rest-api-groups.txt b/Documentation/rest-api-groups.txt
index 6289e5a..e96607c 100644
--- a/Documentation/rest-api-groups.txt
+++ b/Documentation/rest-api-groups.txt
@@ -1239,3 +1239,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/rest-api-plugins.txt b/Documentation/rest-api-plugins.txt
index 2cc80f0..f5b6809 100644
--- a/Documentation/rest-api-plugins.txt
+++ b/Documentation/rest-api-plugins.txt
@@ -45,12 +45,12 @@
     "delete-project": {
       "kind": "gerritcodereview#plugin",
       "id": "delete-project",
-      "version": "2.8-SNAPSHOT"
+      "version": "2.9-SNAPSHOT"
     },
     "reviewers-by-blame": {
       "kind": "gerritcodereview#plugin",
       "id": "reviewers-by-blame",
-      "version": "2.8-SNAPSHOT",
+      "version": "2.9-SNAPSHOT",
       "disabled": true
     }
   }
@@ -277,3 +277,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 35db183..c2e937a 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -552,9 +552,17 @@
 
 Run the Git garbage collection for the repository of a project.
 
+Options for the Git garbage collection can be specified in the
+request body as a link:#gc-input[GCInput] entity.
+
 .Request
 ----
   POST /projects/plugins%2Freplication/gc HTTP/1.0
+  Content-Type: application/json;charset=UTF-8
+
+  {
+    "show_progress": true
+  }
 ----
 
 The response is the streamed output of the garbage collection.
@@ -1317,6 +1325,19 @@
 Tokens such as `${project}` are not resolved.
 |===========================
 
+[[gc-input]]
+GCInput
+~~~~~~~
+The `GCInput` entity contains information to run the Git garbage
+collection.
+
+[options="header",width="50%",cols="1,^2,4"]
+|=============================
+|Field Name      ||Description
+|`show_progress` |`false` if not set|
+Whether progress information should be shown.
+|=============================
+
 [[head-input]]
 HeadInput
 ~~~~~~~~~
@@ -1515,3 +1536,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/rest-api.txt b/Documentation/rest-api.txt
index 7eed6ef..bedf218 100644
--- a/Documentation/rest-api.txt
+++ b/Documentation/rest-api.txt
@@ -23,6 +23,8 @@
   Plugin related REST endpoints
 link:rest-api-projects.html[/projects/]::
   Project related REST endpoints
+link:rest-api-documentation.html[/Documentation/]::
+  Documentation related REST endpoints
 
 Protocol Details
 ----------------
@@ -169,3 +171,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/user-changeid.txt b/Documentation/user-changeid.txt
index c13faa6..ca60819 100644
--- a/Documentation/user-changeid.txt
+++ b/Documentation/user-changeid.txt
@@ -169,3 +169,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/user-dashboards.txt b/Documentation/user-dashboards.txt
index 0945153..1c5162a 100644
--- a/Documentation/user-dashboards.txt
+++ b/Documentation/user-dashboards.txt
@@ -169,3 +169,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/user-notify.txt b/Documentation/user-notify.txt
index 558dcb5..dd7d4bd 100644
--- a/Documentation/user-notify.txt
+++ b/Documentation/user-notify.txt
@@ -145,3 +145,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 7c4ca52..85c0711 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -74,6 +74,13 @@
 Either a legacy numerical 'ID' such as 15183, or a newer style
 Change-Id that was scraped out of the commit message.
 
+[[conflicts]]
+conflicts:'ID'::
++
+Changes that conflict with change 'ID'. Change 'ID' can be specified
+as a legacy numerical 'ID' such as 15183, or a newer style Change-Id
+that was scraped out of the commit message.
+
 [[owner]]
 owner:'USER'::
 +
@@ -110,6 +117,12 @@
 link:http://www.brics.dk/automaton/[dk.brics.automaton
 library] is used for evaluation of such patterns.
 
+[[parentproject]]
+parentproject:'PROJECT'::
++
+Changes occurring in 'PROJECT' or in one of the child projects of
+'PROJECT'.
+
 [[branch]]
 branch:'BRANCH'::
 +
@@ -454,3 +467,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/user-signedoffby.txt b/Documentation/user-signedoffby.txt
index 56858bf..c5ae135 100644
--- a/Documentation/user-signedoffby.txt
+++ b/Documentation/user-signedoffby.txt
@@ -175,3 +175,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/user-submodules.txt b/Documentation/user-submodules.txt
index 6aab43f..fbf45d7 100644
--- a/Documentation/user-submodules.txt
+++ b/Documentation/user-submodules.txt
@@ -187,3 +187,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/user-upload.txt b/Documentation/user-upload.txt
index cbb152c3..17c4acf 100644
--- a/Documentation/user-upload.txt
+++ b/Documentation/user-upload.txt
@@ -366,6 +366,15 @@
   git push ssh://john.doe@git.example.com:29418/kernel/common HEAD:refs/for/master%base=$(git rev-parse origin/master)
 ====
 
+It is also possible to specify more than one '%base' argument.
+This may be useful when pushing a merge commit. Note that the '%'
+character has only to be provided once, for the first '%base'
+argument:
+
+====
+  git push ssh://john.doe@git.example.com:29418/kernel/common HEAD:refs/for/master%base=commit-id1,base=commit-id2
+====
+
 
 repo upload
 -----------
@@ -445,3 +454,6 @@
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/ReleaseNotes/ReleaseNotes-2.9.txt b/ReleaseNotes/ReleaseNotes-2.9.txt
new file mode 100644
index 0000000..e4aa124
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.9.txt
@@ -0,0 +1,79 @@
+Release notes for Gerrit 2.9
+============================
+
+
+Gerrit 2.9 is now available:
+
+link:https://gerrit-releases.storage.googleapis.com/gerrit-2.9.war[
+https://gerrit-releases.storage.googleapis.com/gerrit-2.9.war]
+
+
+Schema Change
+-------------
+
+
+*WARNING:* This release contains schema changes.  To upgrade:
+----
+  java -jar gerrit.war init -d site_path
+----
+
+*WARNING:* Upgrading to 2.9.x requires the server be first upgraded to 2.1.7 (or
+a later 2.1.x version), and then to 2.9.x.  If you are upgrading from 2.2.x.x or
+later, you may ignore this warning and upgrade directly to 2.9.x.
+
+
+Release Highlights
+------------------
+
+
+* 'Gerrit Inspector' for interactive inspection and troubleshooting of a running
+Gerrit instance.
+
+
+New Features
+------------
+
+
+ssh
+~~~
+
+
+* New `--all-reviewers` option on the `query` command allowing query results
+to include information about all reviewers added on the change.
+
+* New link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/cmd-apropos.html[
+`apropos` command] to search the Gerrit documentation.
+
+
+REST API
+~~~~~~~~
+
+
+Documentation
+^^^^^^^^^^^^^
+
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/rest-api-documentation#search-documentation.html[
+Search documentation].
+
+Daemon
+~~~~~~
+
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/dev-inspector.html[
+Gerrit Inspector]: interactive Jython shell.
++
+New `-s` option is added to the Daemon to start an interactive Jython shell for inspection and
+troubleshooting of live data of the Gerrit instance.
+
+
+Bug Fixes
+---------
+
+
+Configuration
+~~~~~~~~~~~~~
+
+
+* The number of accounts shown on the 'Become Any Account' login
+screen is increased from 5 to 100.
diff --git a/ReleaseNotes/index.txt b/ReleaseNotes/index.txt
index 28b9f65..effb0d4 100644
--- a/ReleaseNotes/index.txt
+++ b/ReleaseNotes/index.txt
@@ -1,6 +1,11 @@
 Gerrit Code Review - Release Notes
 ==================================
 
+[[2_9]]
+Version 2.9.x
+-------------
+* link:ReleaseNotes-2.9.html[2.9]
+
 [[2_8]]
 Version 2.8.x
 -------------
diff --git a/VERSION b/VERSION
index 23c7033..6a132bb 100644
--- a/VERSION
+++ b/VERSION
@@ -2,4 +2,4 @@
 # Used by :api_install and :api_deploy targets
 # when talking to the destination repository.
 #
-GERRIT_VERSION = '2.8-SNAPSHOT'
+GERRIT_VERSION = '2.9-SNAPSHOT'
diff --git a/contrib/themes/spotify/static/background-gradient.png b/contrib/themes/spotify/static/background-gradient.png
index b40f35b..3b9422e 100644
--- a/contrib/themes/spotify/static/background-gradient.png
+++ b/contrib/themes/spotify/static/background-gradient.png
Binary files differ
diff --git a/contrib/themes/spotify/static/logo.png b/contrib/themes/spotify/static/logo.png
index bfe1fce..c231031 100644
--- a/contrib/themes/spotify/static/logo.png
+++ b/contrib/themes/spotify/static/logo.png
Binary files differ
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 784b461..b24ec47 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -55,10 +55,14 @@
   }
 
   private void beforeTest(Config cfg, boolean memory) throws Exception {
-    server = GerritServer.start(cfg, memory);
+    server = startServer(cfg, memory);
     server.getTestInjector().injectMembers(this);
   }
 
+  protected GerritServer startServer(Config cfg, boolean memory) throws Exception {
+    return GerritServer.start(cfg, memory);
+  }
+
   private void afterTest() throws Exception {
     server.stop();
   }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTestWithSecondaryIndex.java
similarity index 64%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTestWithSecondaryIndex.java
index a5371d2..7ab5911 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTestWithSecondaryIndex.java
@@ -12,19 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.acceptance;
 
-import com.google.common.collect.Maps;
+import org.eclipse.jgit.lib.Config;
 
-import java.util.Map;
+public class AbstractDaemonTestWithSecondaryIndex extends AbstractDaemonTest {
 
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
+  @Override
+  protected GerritServer startServer(Config cfg, boolean memory)
+      throws Exception {
+    return GerritServer.start(cfg, memory, true);
   }
 }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
new file mode 100644
index 0000000..63bdfd2
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
@@ -0,0 +1,186 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance;
+
+import com.google.common.collect.Maps;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.RequestCleanup;
+import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
+import com.google.gerrit.server.util.RequestContext;
+import com.google.gerrit.server.util.ThreadLocalRequestContext;
+import com.google.gerrit.server.util.ThreadLocalRequestScopePropagator;
+import com.google.gerrit.server.util.TimeUtil;
+import com.google.gwtorm.server.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Key;
+import com.google.inject.OutOfScopeException;
+import com.google.inject.Provider;
+import com.google.inject.Scope;
+import com.google.inject.util.Providers;
+
+import java.util.Map;
+
+/** Guice scopes for state during an Acceptance Test connection. */
+public class AcceptanceTestRequestScope {
+  private static final Key<RequestCleanup> RC_KEY =
+      Key.get(RequestCleanup.class);
+
+  private static final Key<RequestScopedReviewDbProvider> DB_KEY =
+      Key.get(RequestScopedReviewDbProvider.class);
+
+  public class Context implements RequestContext {
+    private final RequestCleanup cleanup = new RequestCleanup();
+    private final Map<Key<?>, Object> map = Maps.newHashMap();
+    private final SchemaFactory<ReviewDb> schemaFactory;
+    private final SshSession session;
+    private final CurrentUser user;
+
+    final long created;
+    volatile long started;
+    volatile long finished;
+
+    private Context(SchemaFactory<ReviewDb> sf, SshSession s,
+        CurrentUser u, long at) {
+      schemaFactory = sf;
+      session = s;
+      user = u;
+      created = started = finished = at;
+      map.put(RC_KEY, cleanup);
+      map.put(DB_KEY, new RequestScopedReviewDbProvider(
+          schemaFactory,
+          Providers.of(cleanup)));
+    }
+
+    private Context(Context p, SshSession s, CurrentUser c) {
+      this(p.schemaFactory, s, c, p.created);
+      started = p.started;
+      finished = p.finished;
+    }
+
+    SshSession getSession() {
+      return session;
+    }
+
+    @Override
+    public CurrentUser getCurrentUser() {
+      if (user == null) {
+        throw new IllegalStateException("user == null, forgot to set it?");
+      }
+      return user;
+    }
+
+    @Override
+    public Provider<ReviewDb> getReviewDbProvider() {
+      return (RequestScopedReviewDbProvider) map.get(DB_KEY);
+    }
+
+    synchronized <T> T get(Key<T> key, Provider<T> creator) {
+      @SuppressWarnings("unchecked")
+      T t = (T) map.get(key);
+      if (t == null) {
+        t = creator.get();
+        map.put(key, t);
+      }
+      return t;
+    }
+  }
+
+  static class ContextProvider implements Provider<Context> {
+    @Override
+    public Context get() {
+      return requireContext();
+    }
+  }
+
+  static class SshSessionProvider implements Provider<SshSession> {
+    @Override
+    public SshSession get() {
+      return requireContext().getSession();
+    }
+  }
+
+  static class Propagator extends ThreadLocalRequestScopePropagator<Context> {
+    private final AcceptanceTestRequestScope atrScope;
+
+    @Inject
+    Propagator(AcceptanceTestRequestScope atrScope, ThreadLocalRequestContext local,
+        Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
+      super(REQUEST, current, local, dbProviderProvider);
+      this.atrScope = atrScope;
+    }
+
+    @Override
+    protected Context continuingContext(Context ctx) {
+      // The cleanup is not chained, since the RequestScopePropagator executors
+      // the Context's cleanup when finished executing.
+      return atrScope.newContinuingContext(ctx);
+    }
+  }
+
+  private static final ThreadLocal<Context> current =
+      new ThreadLocal<Context>();
+
+  private static Context requireContext() {
+    final Context ctx = current.get();
+    if (ctx == null) {
+      throw new OutOfScopeException("Not in command/request");
+    }
+    return ctx;
+  }
+
+  private final ThreadLocalRequestContext local;
+
+  @Inject
+  AcceptanceTestRequestScope(ThreadLocalRequestContext local) {
+    this.local = local;
+  }
+
+  public Context newContext(SchemaFactory<ReviewDb> sf, SshSession s, CurrentUser user) {
+    return new Context(sf, s, user, TimeUtil.nowMs());
+  }
+
+  private Context newContinuingContext(Context ctx) {
+    return new Context(ctx, ctx.getSession(), ctx.getCurrentUser());
+  }
+
+  public Context set(Context ctx) {
+    Context old = current.get();
+    current.set(ctx);
+    local.setContext(ctx);
+    return old;
+  }
+
+  /** Returns exactly one instance per command executed. */
+  static final Scope REQUEST = new Scope() {
+    public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
+      return new Provider<T>() {
+        public T get() {
+          return requireContext().get(key, creator);
+        }
+
+        @Override
+        public String toString() {
+          return String.format("%s[%s]", creator, REQUEST);
+        }
+      };
+    }
+
+    @Override
+    public String toString() {
+      return "Acceptance Test Scope.REQUEST";
+    }
+  };
+}
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 daf9d38..30eb93c 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
@@ -15,10 +15,13 @@
 package com.google.gerrit.acceptance;
 
 import com.google.common.collect.ImmutableList;
+import com.google.gerrit.lucene.LuceneIndexModule;
 import com.google.gerrit.pgm.Daemon;
 import com.google.gerrit.pgm.Init;
+import com.google.gerrit.pgm.Reindex;
 import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.index.ChangeSchemas;
 import com.google.gerrit.server.util.SocketUtil;
 import com.google.inject.Injector;
 import com.google.inject.Key;
@@ -48,6 +51,12 @@
 
   /** Returns fully started Gerrit server */
   static GerritServer start(Config base, boolean memory) throws Exception {
+    return start(base, memory, false);
+  }
+
+  /** Returns fully started Gerrit server */
+  static GerritServer start(Config base, boolean memory, boolean index)
+      throws Exception {
     final CyclicBarrier serverStarted = new CyclicBarrier(2);
     final Daemon daemon = new Daemon(new Runnable() {
       public void run() {
@@ -69,11 +78,25 @@
       mergeTestConfig(cfg);
       cfg.setBoolean("httpd", null, "requestLog", false);
       cfg.setBoolean("sshd", null, "requestLog", false);
+      if (index) {
+        cfg.setString("index", null, "type", "lucene");
+        cfg.setBoolean("index", "lucene", "testInmemory", true);
+        daemon.setLuceneModule(new LuceneIndexModule(
+            ChangeSchemas.getLatest().getVersion(),
+            Runtime.getRuntime().availableProcessors(), null));
+      }
       daemon.setDatabaseForTesting(ImmutableList.<Module>of(
           new InMemoryTestingDatabaseModule(cfg)));
       daemon.start();
     } else {
-      site = initSite(base);
+      Config cfg = base != null ? base : new Config();
+      if (index) {
+        cfg.setString("index", null, "type", "lucene");
+      }
+      site = initSite(cfg);
+      if (index) {
+        reindex(site);
+      }
       daemonService = Executors.newSingleThreadExecutor();
       daemonService.submit(new Callable<Void>() {
         public Void call() throws Exception {
@@ -84,7 +107,7 @@
             serverStarted.reset();
           }
           return null;
-        };
+        }
       });
       serverStarted.await();
       System.out.println("Gerrit Server Started");
@@ -114,6 +137,15 @@
     return tmp;
   }
 
+  /** Runs the reindex command. Works only if the site is not currently running. */
+  private static void reindex(File site) throws Exception {
+    Reindex reindex = new Reindex();
+    int rc = reindex.main(new String[] {"-d", site.getPath()});
+    if (rc != 0) {
+      throw new RuntimeException("Reindex failed");
+    }
+  }
+
   private static void mergeTestConfig(Config cfg)
       throws IOException {
     InetSocketAddress http = newPort();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
index 5576c4f..8548b5c 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
@@ -34,7 +34,6 @@
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.OrmRuntimeException;
 import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import com.google.inject.Provides;
 import com.google.inject.Singleton;
@@ -46,7 +45,7 @@
 
 import java.io.File;
 
-class InMemoryTestingDatabaseModule extends AbstractModule {
+class InMemoryTestingDatabaseModule extends LifecycleModule {
   private final Config cfg;
 
   InMemoryTestingDatabaseModule(Config cfg) {
@@ -71,12 +70,7 @@
     bind(new TypeLiteral<SchemaFactory<ReviewDb>>() {})
       .to(InMemoryDatabase.class);
 
-    install(new LifecycleModule() {
-      @Override
-      protected void configure() {
-        listener().to(CreateDatabase.class);
-      }
-    });
+    listener().to(CreateDatabase.class);
 
     bind(SitePaths.class);
     bind(TrackingFooters.class)
@@ -126,4 +120,4 @@
       mem.drop();
     }
   }
-}
\ No newline at end of file
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/TestAccount.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/TestAccount.java
index b85ffea..31ed136 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/TestAccount.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/TestAccount.java
@@ -58,4 +58,8 @@
         server.getHttpAddress().getAddress().getHostAddress(),
         server.getHttpAddress().getPort());
   }
+
+  public Account.Id getId() {
+    return id;
+  }
 }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/UseGerritConfigAnnotationTest.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/UseGerritConfigAnnotationTest.java
index 0931e12..88d46a4 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/UseGerritConfigAnnotationTest.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/UseGerritConfigAnnotationTest.java
@@ -37,7 +37,7 @@
   @Test
   @GerritConfigs({
       @GerritConfig(name="x.y", value="z"),
-      @GerritConfig(name="a.b", value="c"),
+      @GerritConfig(name="a.b", value="c")
   })
   public void testMultiple() {
     assertEquals("z", serverConfig.getString("x", null, "y"));
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/BUCK b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/BUCK
new file mode 100644
index 0000000..94e6f6a
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/BUCK
@@ -0,0 +1,6 @@
+include_defs('//gerrit-acceptance-tests/tests.defs')
+
+acceptance_tests(
+  srcs = glob(['*IT.java']),
+  deps = ['//gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git:util'],
+)
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
new file mode 100644
index 0000000..dc65470
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -0,0 +1,145 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.api.change;
+
+import static com.google.gerrit.acceptance.git.GitUtil.cloneProject;
+import static com.google.gerrit.acceptance.git.GitUtil.createProject;
+import static com.google.gerrit.acceptance.git.GitUtil.initSsh;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
+import com.google.gerrit.acceptance.AccountCreator;
+import com.google.gerrit.acceptance.SshSession;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.git.PushOneCommit;
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gwtorm.server.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.util.Providers;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class ChangeIT extends AbstractDaemonTest {
+
+  @Inject
+  private AccountCreator accounts;
+
+  @Inject
+  private SchemaFactory<ReviewDb> reviewDbProvider;
+
+  @Inject
+  private GerritApi gApi;
+
+  @Inject
+  private AcceptanceTestRequestScope atrScope;
+
+  @Inject
+  private IdentifiedUser.GenericFactory identifiedUserFactory;
+
+  private TestAccount admin;
+  private Git git;
+  private ReviewDb db;
+
+  @Before
+  public void setUp() throws Exception {
+    admin = accounts.admin();
+    initSsh(admin);
+    Project.NameKey project = new Project.NameKey("p");
+    SshSession sshSession = new SshSession(server, admin);
+    createProject(sshSession, project.get());
+    git = cloneProject(sshSession.getUrl() + "/" + project.get());
+    db = reviewDbProvider.open();
+    atrScope.set(atrScope.newContext(reviewDbProvider, sshSession,
+        identifiedUserFactory.create(Providers.of(db), admin.getId())));
+  }
+
+  @After
+  public void cleanup() {
+    db.close();
+  }
+
+  @Test
+  public void abandon() throws GitAPIException,
+      IOException, RestApiException {
+    PushOneCommit.Result r = createChange();
+    gApi.changes()
+        .id("p~master~" + r.getChangeId())
+        .abandon();
+  }
+
+  @Test
+  public void restore() throws GitAPIException,
+      IOException, RestApiException {
+    PushOneCommit.Result r = createChange();
+    gApi.changes()
+        .id("p~master~" + r.getChangeId())
+        .abandon();
+    gApi.changes()
+        .id("p~master~" + r.getChangeId())
+        .restore();
+  }
+
+  @Test
+  public void revert() throws GitAPIException,
+      IOException, RestApiException {
+    PushOneCommit.Result r = createChange();
+    gApi.changes()
+        .id("p~master~" + r.getChangeId())
+        .revision(r.getCommit().name())
+        .review(approve());
+    gApi.changes()
+        .id("p~master~" + r.getChangeId())
+        .revision(r.getCommit().name())
+        .submit();
+    gApi.changes()
+        .id("p~master~" + r.getChangeId())
+        .revert();
+  }
+
+  // Change is already up to date
+  @Test(expected = ResourceConflictException.class)
+  public void rebase() throws GitAPIException,
+      IOException, RestApiException {
+    PushOneCommit.Result r = createChange();
+    gApi.changes()
+        .id("p~master~" + r.getChangeId())
+        .revision(r.getCommit().name())
+        .rebase();
+  }
+
+  private PushOneCommit.Result createChange() throws GitAPIException,
+      IOException {
+    PushOneCommit push = new PushOneCommit(db, admin.getIdent());
+    return push.to(git, "refs/for/master");
+  }
+
+  private static ReviewInput approve() {
+    return new ReviewInput()
+      .message("Looks good!")
+      .label("Code-Review", 2);
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/BUCK b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/BUCK
new file mode 100644
index 0000000..94e6f6a
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/BUCK
@@ -0,0 +1,6 @@
+include_defs('//gerrit-acceptance-tests/tests.defs')
+
+acceptance_tests(
+  srcs = glob(['*IT.java']),
+  deps = ['//gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git:util'],
+)
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java
new file mode 100644
index 0000000..d60a904
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -0,0 +1,143 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.api.revision;
+
+import static com.google.gerrit.acceptance.git.GitUtil.cloneProject;
+import static com.google.gerrit.acceptance.git.GitUtil.createProject;
+import static com.google.gerrit.acceptance.git.GitUtil.initSsh;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
+import com.google.gerrit.acceptance.AccountCreator;
+import com.google.gerrit.acceptance.SshSession;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.git.PushOneCommit;
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.api.changes.RevisionApi;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gwtorm.server.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.util.Providers;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class RevisionIT extends AbstractDaemonTest {
+
+  @Inject
+  private AccountCreator accounts;
+
+  @Inject
+  private SchemaFactory<ReviewDb> reviewDbProvider;
+
+  @Inject
+  private GerritApi gApi;
+
+  @Inject
+  private AcceptanceTestRequestScope atrScope;
+
+  @Inject
+  private IdentifiedUser.GenericFactory identifiedUserFactory;
+
+  private TestAccount admin;
+  private Git git;
+  private ReviewDb db;
+
+  @Before
+  public void setUp() throws Exception {
+    admin = accounts.admin();
+    initSsh(admin);
+    Project.NameKey project = new Project.NameKey("p");
+    SshSession sshSession = new SshSession(server, admin);
+    createProject(sshSession, project.get());
+    git = cloneProject(sshSession.getUrl() + "/" + project.get());
+    db = reviewDbProvider.open();
+    atrScope.set(atrScope.newContext(reviewDbProvider, sshSession,
+        identifiedUserFactory.create(Providers.of(db), admin.getId())));
+  }
+
+  @After
+  public void cleanup() {
+    db.close();
+  }
+
+  @Test
+  public void reviewTriplet() throws GitAPIException,
+      IOException, RestApiException {
+    PushOneCommit.Result r = createChange();
+    gApi.changes()
+        .id("p~master~" + r.getChangeId())
+        .revision(r.getCommit().name())
+        .review(approve());
+  }
+
+  @Test
+  public void reviewId() throws GitAPIException,
+      IOException, RestApiException {
+    PushOneCommit.Result r = createChange();
+    gApi.changes()
+        .id(r.getChangeId())
+        .current()
+        .review(approve());
+  }
+
+  @Test
+  public void submit() throws GitAPIException,
+      IOException, RestApiException {
+    PushOneCommit.Result r = createChange();
+    RevisionApi rApi = gApi.changes()
+        .id("p~master~" + r.getChangeId())
+        .current();
+    rApi.review(approve());
+    rApi.submit();
+  }
+
+  @Test
+  public void deleteDraft() throws GitAPIException,
+      IOException, RestApiException {
+    PushOneCommit.Result r = createDraft();
+    gApi.changes()
+        .id(r.getChangeId())
+        .revision(r.getCommit().name())
+        .delete();
+  }
+
+  private PushOneCommit.Result createChange() throws GitAPIException,
+      IOException {
+    PushOneCommit push = new PushOneCommit(db, admin.getIdent());
+    return push.to(git, "refs/for/master");
+  }
+
+  private PushOneCommit.Result createDraft() throws GitAPIException,
+      IOException {
+    PushOneCommit push = new PushOneCommit(db, admin.getIdent());
+    return push.to(git, "refs/drafts/master");
+  }
+
+  private static ReviewInput approve() {
+    return new ReviewInput()
+      .message("Looks good!")
+      .label("Code-Review", 2);
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/GitUtil.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/GitUtil.java
index c17598f..3d1b009 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/GitUtil.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/GitUtil.java
@@ -28,6 +28,7 @@
 import org.eclipse.jgit.api.CheckoutCommand;
 import org.eclipse.jgit.api.CloneCommand;
 import org.eclipse.jgit.api.CommitCommand;
+import org.eclipse.jgit.api.FetchCommand;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.PushCommand;
 import org.eclipse.jgit.api.errors.GitAPIException;
@@ -105,10 +106,15 @@
   }
 
   public static Git cloneProject(String url) throws GitAPIException, IOException {
+    return cloneProject(url, true);
+  }
+
+  public static Git cloneProject(String url, boolean checkout) throws GitAPIException, IOException {
     final File gitDir = TempFileUtil.createTempDirectory();
     final CloneCommand cloneCmd = Git.cloneRepository();
     cloneCmd.setURI(url);
     cloneCmd.setDirectory(gitDir);
+    cloneCmd.setNoCheckout(!checkout);
     return cloneCmd.call();
   }
 
@@ -178,6 +184,12 @@
     }
   }
 
+  public static void fetch(Git git, String spec) throws GitAPIException {
+    FetchCommand fetch = git.fetch();
+    fetch.setRefSpecs(new RefSpec(spec));
+    fetch.call();
+  }
+
   public static void checkout(Git git, String name) throws GitAPIException {
     CheckoutCommand checkout = git.checkout();
     checkout.setName(name);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/BUCK b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/BUCK
index 1fca451..8b7d091 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/BUCK
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/BUCK
@@ -11,7 +11,11 @@
 
 java_library(
   name = 'util',
-  srcs = ['AccountAssert.java', 'AccountInfo.java'],
+  srcs = [
+    'AccountAssert.java',
+    'AccountInfo.java',
+    'CapabilityInfo.java',
+  ],
   deps = [
     '//gerrit-acceptance-tests:lib',
     '//gerrit-reviewdb:server',
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/CapabilitiesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/CapabilitiesIT.java
new file mode 100644
index 0000000..e22220a
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/CapabilitiesIT.java
@@ -0,0 +1,150 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.account;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.AccountCreator;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.RestSession;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.google.inject.Inject;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class CapabilitiesIT extends AbstractDaemonTest {
+
+  @Inject
+  private AccountCreator accounts;
+
+  @Inject
+  private AllProjectsName allProjects;
+
+  @Inject
+  private MetaDataUpdate.Server metaDataUpdateFactory;
+
+  @Inject
+  private GroupCache groupCache;
+
+  @Inject
+  private ProjectCache projectCache;
+
+  private RestSession userSession;
+  private RestSession adminSession;
+
+  @Before
+  public void setUp() throws Exception {
+    TestAccount user = accounts.create("user", "user@example.com", "User");
+    TestAccount admin = accounts.admin();
+    userSession = new RestSession(server, user);
+    adminSession = new RestSession(server, admin);
+  }
+
+  @Test
+  public void testCapabilitiesUser() throws IOException,
+      ConfigInvalidException, IllegalArgumentException,
+      IllegalAccessException, NoSuchFieldException,
+      SecurityException {
+    grantAllCapabilities();
+    RestResponse r =
+        userSession.get("/accounts/self/capabilities");
+    int code = r.getStatusCode();
+    assertEquals(code, 200);
+    CapabilityInfo info = (new Gson()).fromJson(r.getReader(),
+        new TypeToken<CapabilityInfo>() {}.getType());
+    for (String c: GlobalCapability.getAllNames()) {
+      if (GlobalCapability.ADMINISTRATE_SERVER.equals(c)) {
+        assertFalse(info.administrateServer);
+      } else if (GlobalCapability.PRIORITY.equals(c)) {
+        assertFalse(info.priority);
+      } else if (GlobalCapability.QUERY_LIMIT.equals(c)) {
+        assertEquals(0, info.queryLimit.min);
+        assertEquals(0, info.queryLimit.max);
+      } else {
+        assertTrue(String.format("capability %s was not granted", c),
+            (Boolean)CapabilityInfo.class.getField(c).get(info));
+      }
+    }
+  }
+
+  @Test
+  public void testCapabilitiesAdmin() throws IOException,
+      ConfigInvalidException, IllegalArgumentException,
+      IllegalAccessException, NoSuchFieldException,
+      SecurityException {
+    RestResponse r =
+        adminSession.get("/accounts/self/capabilities");
+    int code = r.getStatusCode();
+    assertEquals(code, 200);
+    CapabilityInfo info = (new Gson()).fromJson(r.getReader(),
+        new TypeToken<CapabilityInfo>() {}.getType());
+    for (String c: GlobalCapability.getAllNames()) {
+      if (GlobalCapability.PRIORITY.equals(c)) {
+        assertFalse(info.priority);
+      } else if (GlobalCapability.QUERY_LIMIT.equals(c)) {
+        assertEquals(0, info.queryLimit.min);
+        assertEquals(500, info.queryLimit.max);
+      } else if (GlobalCapability.ACCESS_DATABASE.equals(c)) {
+        assertFalse(info.accessDatabase);
+      } else if (GlobalCapability.RUN_AS.equals(c)) {
+        assertFalse(info.runAs);
+      } else {
+        assertTrue(String.format("capability %s was not granted", c),
+            (Boolean)CapabilityInfo.class.getField(c).get(info));
+      }
+    }
+  }
+
+  private void grantAllCapabilities() throws IOException,
+      ConfigInvalidException {
+    MetaDataUpdate md = metaDataUpdateFactory.create(allProjects);
+    md.setMessage("Make super user");
+    ProjectConfig config = ProjectConfig.read(md);
+    AccessSection s = config.getAccessSection(
+        AccessSection.GLOBAL_CAPABILITIES);
+    for (String c: GlobalCapability.getAllNames()) {
+      if (GlobalCapability.ADMINISTRATE_SERVER.equals(c)) {
+        continue;
+      }
+      Permission p = s.getPermission(c, true);
+      AccountGroup projectOwnersGroup = groupCache.get(
+          new AccountGroup.NameKey("Registered Users"));
+      PermissionRule rule = new PermissionRule(
+          config.resolve(projectOwnersGroup));
+      p.add(rule);
+    }
+    config.commit(md);
+    projectCache.evict(config.getProject());
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/CapabilityInfo.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/CapabilityInfo.java
new file mode 100644
index 0000000..0586fe4
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/CapabilityInfo.java
@@ -0,0 +1,40 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.account;
+
+class CapabilityInfo {
+  public boolean accessDatabase;
+  public boolean administrateServer;
+  public boolean createAccount;
+  public boolean createGroup;
+  public boolean createProject;
+  public boolean emailReviewers;
+  public boolean flushCaches;
+  public boolean generateHttpPassword;
+  public boolean killTask;
+  public boolean priority;
+  public QueryLimit queryLimit;
+  public boolean runAs;
+  public boolean runGC;
+  public boolean streamEvents;
+  public boolean viewCaches;
+  public boolean viewConnections;
+  public boolean viewQueue;
+
+  static class QueryLimit {
+    short min;
+    short max;
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index fc2fccd..5e2c0d8 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -28,6 +28,7 @@
 import com.google.gerrit.acceptance.TestAccount;
 import com.google.gerrit.acceptance.git.GitUtil;
 import com.google.gerrit.acceptance.git.PushOneCommit;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.Project.InheritableBoolean;
@@ -171,9 +172,9 @@
   }
 
   private void approve(String changeId) throws IOException {
-    RestResponse r =
-        session.post("/changes/" + changeId + "/revisions/current/review",
-            ReviewInput.approve());
+    RestResponse r = session.post(
+        "/changes/" + changeId + "/revisions/current/review",
+        new ReviewInput().label("Code-Review", 2));
     assertEquals(HttpStatus.SC_OK, r.getStatusCode());
     r.consume();
   }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUCK b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUCK
index 20b1033..5172fa9 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUCK
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUCK
@@ -1,8 +1,26 @@
 include_defs('//gerrit-acceptance-tests/tests.defs')
 
+UTIL_SRCS = [
+  'AccountInfo.java',
+  'ChangeInfo.java',
+  'ChangeMessageInfo.java',
+  'GroupInfo.java',
+  'ProjectConfigInput.java',
+  'SubmitInput.java',
+  'SuggestReviewerInfo.java',
+]
+
+SUBMIT_UTIL_SRCS = [
+  'AbstractSubmit.java',
+  'AbstractSubmitByMerge.java',
+]
+
+NON_TEST = UTIL_SRCS + SUBMIT_UTIL_SRCS
+SUBMIT_TESTS = glob(['Submit*IT.java'], excludes = NON_TEST)
+OTHER_TESTS = glob(['*IT.java'], excludes = SUBMIT_TESTS + NON_TEST)
+
 acceptance_tests(
-  srcs = ['ChangeMessagesIT.java', 'DeleteDraftChangeIT.java',
-          'DeleteDraftPatchSetIT.java'],
+  srcs = OTHER_TESTS,
   deps = [
     ':util',
     '//gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git:util',
@@ -10,18 +28,16 @@
 )
 
 acceptance_tests(
-  srcs = ['SubmitByCherryPickIT.java', 'SubmitByFastForwardIT.java',
-          'SubmitByMergeAlwaysIT.java', 'SubmitByMergeIfNecessaryIT.java',
-          'SubmitByRebaseIfNecessaryIT.java'],
+  srcs = SUBMIT_TESTS,
   deps = [
-    ':submit',
+    ':submit_util',
     '//gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git:util',
   ],
 )
 
 java_library(
-  name = 'submit',
-  srcs = ['AbstractSubmit.java', 'AbstractSubmitByMerge.java'],
+  name = 'submit_util',
+  srcs = SUBMIT_UTIL_SRCS,
   deps = [
     ':util',
     '//gerrit-acceptance-tests:lib',
@@ -31,9 +47,7 @@
 
 java_library(
   name = 'util',
-  srcs = ['AccountInfo.java', 'ChangeInfo.java', 'ChangeMessageInfo.java',
-          'GroupInfo.java', 'ProjectConfigInput.java', 'ReviewInput.java',
-          'SubmitInput.java', 'SuggestReviewerInfo.java'],
+  srcs = UTIL_SRCS,
   deps = [
     '//lib:guava',
     '//gerrit-reviewdb:server',
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ConflictsOperatorIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ConflictsOperatorIT.java
new file mode 100644
index 0000000..1472f38
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ConflictsOperatorIT.java
@@ -0,0 +1,152 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+import static com.google.gerrit.acceptance.git.GitUtil.checkout;
+import static com.google.gerrit.acceptance.git.GitUtil.cloneProject;
+import static com.google.gerrit.acceptance.git.GitUtil.initSsh;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.gerrit.acceptance.AbstractDaemonTestWithSecondaryIndex;
+import com.google.gerrit.acceptance.AccountCreator;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.RestSession;
+import com.google.gerrit.acceptance.SshSession;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.git.GitUtil;
+import com.google.gerrit.acceptance.git.PushOneCommit;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.google.gwtorm.server.SchemaFactory;
+import com.google.inject.Inject;
+
+import com.jcraft.jsch.JSchException;
+
+import org.apache.http.HttpStatus;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Set;
+
+public class ConflictsOperatorIT extends AbstractDaemonTestWithSecondaryIndex {
+
+  @Inject
+  private AccountCreator accounts;
+
+  @Inject
+  private SchemaFactory<ReviewDb> reviewDbProvider;
+
+  private TestAccount admin;
+  private RestSession session;
+  private Project.NameKey project;
+  private ReviewDb db;
+  private int count;
+
+  @Before
+  public void setUp() throws Exception {
+    admin = accounts.admin();
+    session = new RestSession(server, admin);
+    initSsh(admin);
+
+    project = new Project.NameKey("p");
+
+    db = reviewDbProvider.open();
+  }
+
+  @Test
+  public void noConflictingChanges() throws JSchException, IOException,
+      GitAPIException {
+    Git git = createProject();
+    PushOneCommit.Result change = createChange(git, true);
+    createChange(git, false);
+
+    Set<String> changes = queryConflictingChanges(change);
+    assertEquals(0, changes.size());
+  }
+
+  @Test
+  public void conflictingChanges() throws JSchException, IOException,
+      GitAPIException {
+    Git git = createProject();
+    PushOneCommit.Result change = createChange(git, true);
+    PushOneCommit.Result conflictingChange1 = createChange(git, true);
+    PushOneCommit.Result conflictingChange2 = createChange(git, true);
+    createChange(git, false);
+
+    Set<String> changes = queryConflictingChanges(change);
+    assertChanges(changes, conflictingChange1, conflictingChange2);
+  }
+
+  private Git createProject() throws JSchException, IOException,
+      GitAPIException {
+    SshSession sshSession = new SshSession(server, admin);
+    try {
+      GitUtil.createProject(sshSession, project.get(), null, true);
+      return cloneProject(sshSession.getUrl() + "/" + project.get());
+    } finally {
+      sshSession.close();
+    }
+  }
+
+  private PushOneCommit.Result createChange(Git git, boolean conflicting)
+      throws GitAPIException, IOException {
+    checkout(git, "origin/master");
+    String file = conflicting ? "test.txt" : "test-" + count + ".txt";
+    PushOneCommit push =
+        new PushOneCommit(db, admin.getIdent(), "Change " + count, file,
+            "content " + count);
+    count++;
+    return push.to(git, "refs/for/master");
+  }
+
+  private Set<String> queryConflictingChanges(PushOneCommit.Result change)
+      throws IOException {
+    RestResponse r =
+        session.get("/changes/?q=conflicts:" + change.getChangeId());
+    assertEquals(HttpStatus.SC_OK, r.getStatusCode());
+    Set<ChangeInfo> changes =
+        (new Gson()).fromJson(r.getReader(),
+            new TypeToken<Set<ChangeInfo>>() {}.getType());
+    r.consume();
+    return ImmutableSet.copyOf(Iterables.transform(changes,
+        new Function<ChangeInfo, String>() {
+          @Override
+          public String apply(ChangeInfo input) {
+            return input.id;
+          }
+        }));
+  }
+
+  private void assertChanges(Set<String> actualChanges,
+      PushOneCommit.Result... expectedChanges) {
+    assertEquals(expectedChanges.length, actualChanges.size());
+    for (PushOneCommit.Result c : expectedChanges) {
+      assertTrue(actualChanges.contains(id(c)));
+    }
+  }
+
+  private String id(PushOneCommit.Result change) {
+    return project.get() + "~master~" + change.getChangeId();
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
index 1ed4d61..8655b30 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
@@ -97,7 +97,7 @@
   @GerritConfigs(
       {@GerritConfig(name = "suggest.accounts", value = "true"),
        @GerritConfig(name = "suggest.from", value = "1"),
-       @GerritConfig(name = "accounts.visibility", value = "NONE"),
+       @GerritConfig(name = "accounts.visibility", value = "NONE")
       })
   public void suggestReviewersNoResult2() throws GitAPIException, IOException,
       Exception {
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java
index 7057a4f..69adedd 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java
@@ -67,6 +67,7 @@
 
     project2 = new Project.NameKey("p2");
     createProject(sshSession, project2.get());
+    sshSession.close();
 
     session = new RestSession(server, admin);
   }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java
index bf8aac1b..e7440e8 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java
@@ -74,6 +74,7 @@
     createProject(sshSession, p1.get());
     Project.NameKey p2 = new Project.NameKey("p2");
     createProject(sshSession, p2.get());
+    sshSession.close();
     assertEquals(HttpStatus.SC_NOT_FOUND,
         GET("/projects/" + p1.get() + "/children/" + p2.get()).getStatusCode());
   }
@@ -83,6 +84,7 @@
     SshSession sshSession = new SshSession(server, admin);
     Project.NameKey child = new Project.NameKey("p1");
     createProject(sshSession, child.get());
+    sshSession.close();
     RestResponse r = GET("/projects/" + allProjects.get() + "/children/" + child.get());
     assertEquals(HttpStatus.SC_OK, r.getStatusCode());
     ProjectInfo childInfo =
@@ -98,6 +100,7 @@
     createProject(sshSession, child.get());
     Project.NameKey grandChild = new Project.NameKey("p1.1");
     createProject(sshSession, grandChild.get(), child);
+    sshSession.close();
     assertEquals(HttpStatus.SC_NOT_FOUND,
         GET("/projects/" + allProjects.get() + "/children/" + grandChild.get())
             .getStatusCode());
@@ -111,6 +114,7 @@
     createProject(sshSession, child.get());
     Project.NameKey grandChild = new Project.NameKey("p1.1");
     createProject(sshSession, grandChild.get(), child);
+    sshSession.close();
     RestResponse r =
         GET("/projects/" + allProjects.get() + "/children/" + grandChild.get()
             + "?recursive");
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
index 4778662..69bbfda 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
@@ -99,6 +99,7 @@
 
   @After
   public void cleanup() {
+    sshSession.close();
     db.close();
   }
 
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java
index c03ecd3..5feca06 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java
@@ -84,6 +84,7 @@
     Project.NameKey child2 = new Project.NameKey("p2");
     createProject(sshSession, child2.get());
     createProject(sshSession, "p1.1", child1);
+    sshSession.close();
 
     RestResponse r = GET("/projects/" + allProjects.get() + "/children/");
     assertEquals(HttpStatus.SC_OK, r.getStatusCode());
@@ -107,6 +108,7 @@
     createProject(sshSession, child1_1_1.get(), child1_1);
     Project.NameKey child1_1_1_1 = new Project.NameKey("p1.1.1.1");
     createProject(sshSession, child1_1_1_1.get(), child1_1_1);
+    sshSession.close();
 
     RestResponse r = GET("/projects/" + child1.get() + "/children/?recursive");
     assertEquals(HttpStatus.SC_OK, r.getStatusCode());
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java
new file mode 100644
index 0000000..9fd7568
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java
@@ -0,0 +1,147 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.project;
+
+import static com.google.gerrit.acceptance.git.GitUtil.checkout;
+import static com.google.gerrit.acceptance.git.GitUtil.cloneProject;
+import static com.google.gerrit.acceptance.git.GitUtil.createProject;
+import static com.google.gerrit.acceptance.git.GitUtil.fetch;
+import static com.google.gerrit.acceptance.git.GitUtil.initSsh;
+import static org.junit.Assert.assertEquals;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.AccountCreator;
+import com.google.gerrit.acceptance.SshSession;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.git.PushOneCommit;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.config.AllProjectsNameProvider;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gwtorm.server.SchemaFactory;
+import com.google.inject.Inject;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.Config;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class ProjectLevelConfigIT extends AbstractDaemonTest {
+
+  @Inject
+  private AccountCreator accounts;
+
+  @Inject
+  private SchemaFactory<ReviewDb> reviewDbProvider;
+
+  @Inject
+  private ProjectCache projectCache;
+
+  @Inject
+  private AllProjectsNameProvider allProjects;
+
+  private ReviewDb db;
+  private TestAccount admin;
+  private SshSession sshSession;
+  private String project;
+  private Git git;
+
+  @Before
+  public void setUp() throws Exception {
+    admin = accounts.admin();
+    initSsh(admin);
+    sshSession = new SshSession(server, admin);
+
+    project = "p";
+    createProject(sshSession, project, null, true);
+    git = cloneProject(sshSession.getUrl() + "/" + project);
+    fetch(git, GitRepositoryManager.REF_CONFIG + ":refs/heads/config");
+    checkout(git, "refs/heads/config");
+
+    db = reviewDbProvider.open();
+  }
+
+  @After
+  public void cleanup() {
+    db.close();
+  }
+
+  @Test
+  public void accessProjectSpecificConfig() throws GitAPIException, IOException {
+    String configName = "test.config";
+    Config cfg = new Config();
+    cfg.setString("s1", null, "k1", "v1");
+    cfg.setString("s2", "ss", "k2", "v2");
+    PushOneCommit push =
+        new PushOneCommit(db, admin.getIdent(), "Create Project Level Config",
+            configName, cfg.toText());
+    push.to(git, GitRepositoryManager.REF_CONFIG);
+
+    ProjectState state = projectCache.get(new Project.NameKey(project));
+    assertEquals(cfg.toText(), state.getConfig(configName).get().toText());
+  }
+
+  @Test
+  public void nonExistingConfig() {
+    ProjectState state = projectCache.get(new Project.NameKey(project));
+    assertEquals("", state.getConfig("test.config").get().toText());
+  }
+
+  @Test
+  public void withInheritance() throws GitAPIException, IOException {
+    String configName = "test.config";
+
+    Config parentCfg = new Config();
+    parentCfg.setString("s1", null, "k1", "parentValue1");
+    parentCfg.setString("s1", null, "k2", "parentValue2");
+    parentCfg.setString("s2", "ss", "k3", "parentValue3");
+    parentCfg.setString("s2", "ss", "k4", "parentValue4");
+
+    Git parentGit =
+        cloneProject(sshSession.getUrl() + "/" + allProjects.get().get(), false);
+    fetch(parentGit, GitRepositoryManager.REF_CONFIG + ":refs/heads/config");
+    checkout(parentGit, "refs/heads/config");
+    PushOneCommit push =
+        new PushOneCommit(db, admin.getIdent(), "Create Project Level Config",
+            configName, parentCfg.toText());
+    push.to(parentGit, GitRepositoryManager.REF_CONFIG);
+
+    Config cfg = new Config();
+    cfg.setString("s1", null, "k1", "childValue1");
+    cfg.setString("s2", "ss", "k3", "childValue2");
+    push = new PushOneCommit(db, admin.getIdent(), "Create Project Level Config",
+        configName, cfg.toText());
+    push.to(git, GitRepositoryManager.REF_CONFIG);
+
+    ProjectState state = projectCache.get(new Project.NameKey(project));
+
+    Config expectedCfg = new Config();
+    expectedCfg.setString("s1", null, "k1", "childValue1");
+    expectedCfg.setString("s1", null, "k2", "parentValue2");
+    expectedCfg.setString("s2", "ss", "k3", "childValue2");
+    expectedCfg.setString("s2", "ss", "k4", "parentValue4");
+
+    assertEquals(expectedCfg.toText(), state.getConfig(configName)
+        .getWithInheritance().toText());
+
+    assertEquals(cfg.toText(), state.getConfig(configName).get().toText());
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
index f10258c..24b9f51 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
@@ -35,6 +35,7 @@
 
 import com.jcraft.jsch.JSchException;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -83,6 +84,11 @@
     createProject(sshSession, project3.get());
   }
 
+  @After
+  public void cleanup() {
+    sshSession.close();
+  }
+
   @Test
   @UseLocalDisk
   public void testGc() throws JSchException, IOException {
@@ -110,6 +116,7 @@
     SshSession s = new SshSession(server, accounts.create("user", "user@example.com", "User"));
     s.exec("gerrit gc --all");
     assertError("Capability runGC is required to access this resource", s.getError());
+    s.close();
   }
 
   @Test
diff --git a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/DefaultCacheFactory.java b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/DefaultCacheFactory.java
index 1c850d1..4478cc9 100644
--- a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/DefaultCacheFactory.java
+++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/DefaultCacheFactory.java
@@ -27,7 +27,6 @@
 import com.google.gerrit.server.cache.PersistentCacheFactory;
 import com.google.gerrit.server.cache.h2.H2CacheImpl.ValueHolder;
 import com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.inject.Inject;
 
@@ -39,13 +38,7 @@
   public static class Module extends LifecycleModule {
     @Override
     protected void configure() {
-      install(new FactoryModule() {
-        @Override
-        protected void configure() {
-          factory(ForwardingRemovalListener.Factory.class);
-        }
-      });
-
+      factory(ForwardingRemovalListener.Factory.class);
       bind(DefaultCacheFactory.class);
       bind(MemoryCacheFactory.class).to(DefaultCacheFactory.class);
       bind(PersistentCacheFactory.class).to(H2CacheFactory.class);
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/changes/Side.java b/gerrit-common/src/main/java/com/google/gerrit/common/changes/Side.java
index 777467d..4a9ddf8 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/changes/Side.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/changes/Side.java
@@ -16,5 +16,5 @@
 
 /** The side on which a comment was added. */
 public enum Side {
-  PARENT, REVISION;
+  PARENT, REVISION
 }
\ No newline at end of file
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java
index 69263d9..b5e2b2f 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java
@@ -269,4 +269,24 @@
   public int hashCode() {
     return name.hashCode();
   }
+
+  @Override
+  public String toString() {
+    StringBuilder bldr = new StringBuilder();
+    bldr.append(name)
+        .append(" ");
+    if (exclusiveGroup) {
+      bldr.append("[exclusive] ");
+    }
+    bldr.append("[");
+    Iterator<PermissionRule> it = getRules().iterator();
+    while (it.hasNext()) {
+      bldr.append(it.next());
+      if (it.hasNext()) {
+        bldr.append(", ");
+      }
+    }
+    bldr.append("]");
+    return bldr.toString();
+  }
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java
index a7a66b1..bd05baf 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java
@@ -20,7 +20,7 @@
   public static enum Action {
     ALLOW, DENY, BLOCK,
 
-    INTERACTIVE, BATCH;
+    INTERACTIVE, BATCH
   }
 
   protected Action action = Action.ALLOW;
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAccess.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAccess.java
index ee6cc95..dd6c22b 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAccess.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAccess.java
@@ -28,6 +28,7 @@
   protected Set<String> ownerOf;
   protected boolean isConfigVisible;
   protected boolean canUpload;
+  protected boolean canChangeParent;
   protected LabelTypes labelTypes;
   protected Map<String, String> capabilities;
 
@@ -107,6 +108,14 @@
     this.canUpload = canUpload;
   }
 
+  public boolean canChangeParent() {
+    return canChangeParent;
+  }
+
+  public void setCanChangeParent(boolean canChangeParent) {
+    this.canChangeParent = canChangeParent;
+  }
+
   public LabelTypes getLabelTypes() {
     return labelTypes;
   }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java
index 44f60861..652acac 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java
@@ -33,11 +33,11 @@
   @Audit
   @SignInRequired
   void changeProjectAccess(Project.NameKey projectName, String baseRevision,
-      String message, List<AccessSection> sections,
+      String message, List<AccessSection> sections, Project.NameKey parentProjectName,
       AsyncCallback<ProjectAccess> callback);
 
   @SignInRequired
   void reviewProjectAccess(Project.NameKey projectName, String baseRevision,
-      String message, List<AccessSection> sections,
+      String message, List<AccessSection> sections, Project.NameKey parentProjectName,
       AsyncCallback<Change.Id> callback);
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ReviewerInfo.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ReviewerInfo.java
index 063f13b..28a8340 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ReviewerInfo.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ReviewerInfo.java
@@ -51,7 +51,7 @@
       if (accountInfo.getPreferredEmail() != null) {
         return accountInfo.getPreferredEmail();
       }
-      return accountInfo.getFullName().toString();
+      return accountInfo.getFullName();
     }
     return groupReference.getName();
   }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/SubmitRecord.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/SubmitRecord.java
index 365f6a9..0d02fe0 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/SubmitRecord.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/SubmitRecord.java
@@ -37,7 +37,7 @@
      * <p>
      * Additional detail may be available in {@link SubmitRecord#errorMessage}.
      */
-    RULE_ERROR;
+    RULE_ERROR
   }
 
   public Status status;
@@ -78,7 +78,7 @@
        * The likely cause is access has not been granted correctly by the
        * project owner or site administrator.
        */
-      IMPOSSIBLE;
+      IMPOSSIBLE
     }
 
     public String label;
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/UpdateParentFailedException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/UpdateParentFailedException.java
new file mode 100644
index 0000000..320d055
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/errors/UpdateParentFailedException.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.common.errors;
+
+/** Error indicating that updating a parent project failed. */
+public class UpdateParentFailedException extends Exception {
+  private static final long serialVersionUID = 1L;
+
+  public static final String MESSAGE = "Update Parent Project Failed: ";
+
+  public UpdateParentFailedException(final String message,
+      final Throwable why) {
+    super(MESSAGE + ": " + message, why);
+  }
+}
diff --git a/gerrit-common/src/test/java/com/google/gerrit/common/data/EncodePathSeparatorTest.java b/gerrit-common/src/test/java/com/google/gerrit/common/data/EncodePathSeparatorTest.java
index e00fa48..7c662ae 100644
--- a/gerrit-common/src/test/java/com/google/gerrit/common/data/EncodePathSeparatorTest.java
+++ b/gerrit-common/src/test/java/com/google/gerrit/common/data/EncodePathSeparatorTest.java
@@ -14,10 +14,12 @@
 
 package com.google.gerrit.common.data;
 
-import junit.framework.TestCase;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
 
-public class EncodePathSeparatorTest extends TestCase {
+public class EncodePathSeparatorTest {
 
+  @Test
   public void testDefaultBehaviour() {
 
     GitWebType gitWebType = GitWebType.fromName(null);
@@ -25,6 +27,7 @@
     assertEquals("a/b", gitWebType.replacePathSeparator("a/b"));
   }
 
+  @Test
   public void testExclamationMark() {
 
     GitWebType gitWebType = GitWebType.fromName(null);
diff --git a/gerrit-common/src/test/java/com/google/gerrit/common/data/ParameterizedStringTest.java b/gerrit-common/src/test/java/com/google/gerrit/common/data/ParameterizedStringTest.java
index a2d1279..5000211 100644
--- a/gerrit-common/src/test/java/com/google/gerrit/common/data/ParameterizedStringTest.java
+++ b/gerrit-common/src/test/java/com/google/gerrit/common/data/ParameterizedStringTest.java
@@ -14,12 +14,17 @@
 
 package com.google.gerrit.common.data;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
 import java.util.HashMap;
 import java.util.Map;
 
-public class ParameterizedStringTest extends TestCase {
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class ParameterizedStringTest {
+  @Test
   public void testEmptyString() {
     final ParameterizedString p = new ParameterizedString("");
     assertEquals("", p.getPattern());
@@ -32,6 +37,7 @@
     assertEquals("", p.replace(a));
   }
 
+  @Test
   public void testAsis1() {
     final ParameterizedString p = ParameterizedString.asis("${bar}c");
     assertEquals("${bar}c", p.getPattern());
@@ -45,6 +51,7 @@
     assertEquals("${bar}c", p.replace(a));
   }
 
+  @Test
   public void testReplace1() {
     final ParameterizedString p = new ParameterizedString("${bar}c");
     assertEquals("${bar}c", p.getPattern());
@@ -60,6 +67,7 @@
     assertEquals("frobinatorc", p.replace(a));
   }
 
+  @Test
   public void testReplace2() {
     final ParameterizedString p = new ParameterizedString("a${bar}c");
     assertEquals("a${bar}c", p.getPattern());
@@ -75,6 +83,7 @@
     assertEquals("afrobinatorc", p.replace(a));
   }
 
+  @Test
   public void testReplace3() {
     final ParameterizedString p = new ParameterizedString("a${bar}");
     assertEquals("a${bar}", p.getPattern());
@@ -90,6 +99,7 @@
     assertEquals("afrobinator", p.replace(a));
   }
 
+  @Test
   public void testReplace4() {
     final ParameterizedString p = new ParameterizedString("a${bar}c");
     assertEquals("a${bar}c", p.getPattern());
@@ -104,6 +114,7 @@
     assertEquals("ac", p.replace(a));
   }
 
+  @Test
   public void testReplaceToLowerCase() {
     final ParameterizedString p = new ParameterizedString("${a.toLowerCase}");
     assertEquals(1, p.getParameterNames().size());
@@ -124,6 +135,7 @@
     assertEquals("foo", p.replace(a));
   }
 
+  @Test
   public void testReplaceToUpperCase() {
     final ParameterizedString p = new ParameterizedString("${a.toUpperCase}");
     assertEquals(1, p.getParameterNames().size());
@@ -144,6 +156,7 @@
     assertEquals("FOO", p.replace(a));
   }
 
+  @Test
   public void testReplaceLocalName() {
     final ParameterizedString p = new ParameterizedString("${a.localPart}");
     assertEquals(1, p.getParameterNames().size());
@@ -164,6 +177,7 @@
     assertEquals("foo", p.replace(a));
   }
 
+  @Test
   public void testUndefinedFunctionName() {
     ParameterizedString p =
         new ParameterizedString(
@@ -183,6 +197,7 @@
     assertEquals("hi, FIRSTNAME LASTNAME,your eamil address is 'firstname.lastname'.right?", p.replace(a));
   }
 
+  @Test
   public void testReplaceToUpperCaseToLowerCase() {
     final ParameterizedString p =
         new ParameterizedString("${a.toUpperCase.toLowerCase}");
@@ -204,6 +219,7 @@
     assertEquals("foo@example.com", p.replace(a));
   }
 
+  @Test
   public void testReplaceToUpperCaseLocalName() {
     final ParameterizedString p =
         new ParameterizedString("${a.toUpperCase.localPart}");
@@ -225,6 +241,7 @@
     assertEquals("FOO", p.replace(a));
   }
 
+  @Test
   public void testReplaceToUpperCaseAnUndefinedMethod() {
     final ParameterizedString p =
         new ParameterizedString("${a.toUpperCase.anUndefinedMethod}");
@@ -246,6 +263,7 @@
     assertEquals("FOO@EXAMPLE.COM", p.replace(a));
   }
 
+  @Test
   public void testReplaceLocalNameToUpperCase() {
     final ParameterizedString p =
         new ParameterizedString("${a.localPart.toUpperCase}");
@@ -267,6 +285,7 @@
     assertEquals("FOO", p.replace(a));
   }
 
+  @Test
   public void testReplaceLocalNameToLowerCase() {
     final ParameterizedString p =
         new ParameterizedString("${a.localPart.toLowerCase}");
@@ -288,6 +307,7 @@
     assertEquals("foo", p.replace(a));
   }
 
+  @Test
   public void testReplaceLocalNameAnUndefinedMethod() {
     final ParameterizedString p =
         new ParameterizedString("${a.localPart.anUndefinedMethod}");
@@ -309,6 +329,7 @@
     assertEquals("foo", p.replace(a));
   }
 
+  @Test
   public void testReplaceToLowerCaseToUpperCase() {
     final ParameterizedString p =
         new ParameterizedString("${a.toLowerCase.toUpperCase}");
@@ -330,6 +351,7 @@
     assertEquals("FOO@EXAMPLE.COM", p.replace(a));
   }
 
+  @Test
   public void testReplaceToLowerCaseLocalName() {
     final ParameterizedString p =
         new ParameterizedString("${a.toLowerCase.localPart}");
@@ -351,6 +373,7 @@
     assertEquals("foo", p.replace(a));
   }
 
+  @Test
   public void testReplaceToLowerCaseAnUndefinedMethod() {
     final ParameterizedString p =
         new ParameterizedString("${a.toLowerCase.anUndefinedMethod}");
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/CapabilityScope.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/CapabilityScope.java
index ede8b8c..85f7d15 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/CapabilityScope.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/CapabilityScope.java
@@ -32,5 +32,5 @@
   PLUGIN,
 
   /** Scope is the core server. */
-  CORE;
+  CORE
 }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginCanonicalWebUrl.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginCanonicalWebUrl.java
new file mode 100644
index 0000000..fd14429
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginCanonicalWebUrl.java
@@ -0,0 +1,42 @@
+// 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.annotations;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.inject.BindingAnnotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation applied to a String containing the plugin canonical web URL.
+ * <p>
+ * A plugin or extension may receive this string by Guice injection to discover
+ * the canonical web URL under which the plugin is available:
+ *
+ * <pre>
+ *  @Inject
+ *  MyType(@PluginCanonicalWebUrl String myUrl) {
+ *  ...
+ *  }
+ * </pre>
+ */
+@Target({ElementType.PARAMETER, ElementType.FIELD})
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface PluginCanonicalWebUrl {
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/GerritApi.java
similarity index 63%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/GerritApi.java
index a5371d2..bde58995 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/GerritApi.java
@@ -12,19 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.extensions.api;
 
-import com.google.common.collect.Maps;
+import com.google.gerrit.extensions.api.changes.Changes;
 
-import java.util.Map;
-
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
-  }
+public interface GerritApi {
+  public Changes changes();
 }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/AbandonInput.java
similarity index 63%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/AbandonInput.java
index a5371d2..04a7bc7 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/AbandonInput.java
@@ -12,19 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.extensions.api.changes;
 
-import com.google.common.collect.Maps;
+import com.google.gerrit.extensions.restapi.DefaultInput;
 
-import java.util.Map;
-
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
-  }
+public class AbandonInput {
+  @DefaultInput
+  public String message;
 }
+
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
new file mode 100644
index 0000000..a264009
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
@@ -0,0 +1,34 @@
+// 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.api.changes;
+
+import com.google.gerrit.extensions.restapi.RestApiException;
+
+public interface ChangeApi {
+  String id();
+
+  RevisionApi current() throws RestApiException;
+  RevisionApi revision(int id) throws RestApiException;
+  RevisionApi revision(String id) throws RestApiException;
+
+  void abandon() throws RestApiException;
+  void abandon(AbandonInput in) throws RestApiException;
+
+  void restore() throws RestApiException;
+  void restore(RestoreInput in) throws RestApiException;
+
+  ChangeApi revert() throws RestApiException;
+  ChangeApi revert(RevertInput in) throws RestApiException;
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/Changes.java
similarity index 63%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/Changes.java
index a5371d2..48e9fd3 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/Changes.java
@@ -12,19 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.extensions.api.changes;
 
-import com.google.common.collect.Maps;
+import com.google.gerrit.extensions.restapi.RestApiException;
 
-import java.util.Map;
-
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
-  }
+public interface Changes {
+  ChangeApi id(int id) throws RestApiException;
+  ChangeApi id(String triplet) throws RestApiException;
+  ChangeApi id(String project, String branch, String id)
+      throws RestApiException;
 }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RestoreInput.java
similarity index 63%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RestoreInput.java
index a5371d2..a116dde 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RestoreInput.java
@@ -12,19 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.extensions.api.changes;
 
-import com.google.common.collect.Maps;
+import com.google.gerrit.extensions.restapi.DefaultInput;
 
-import java.util.Map;
-
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
-  }
+public class RestoreInput {
+  @DefaultInput
+  public String message;
 }
+
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevertInput.java
similarity index 63%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevertInput.java
index a5371d2..2c1c688 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevertInput.java
@@ -12,19 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.extensions.api.changes;
 
-import com.google.common.collect.Maps;
+import com.google.gerrit.extensions.restapi.DefaultInput;
 
-import java.util.Map;
-
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
-  }
+public class RevertInput {
+  @DefaultInput
+  public String message;
 }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java
new file mode 100644
index 0000000..5f106fc
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java
@@ -0,0 +1,116 @@
+// 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.api.changes;
+
+import com.google.gerrit.extensions.restapi.DefaultInput;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Input passed to {@code POST /changes/{id}/revisions/{id}/review}. */
+public class ReviewInput {
+  @DefaultInput
+  public String message;
+
+  public Map<String, Short> labels;
+  public Map<String, List<Comment>> comments;
+
+  /**
+   * If true require all labels to be within the user's permitted ranges based
+   * on access controls, attempting to use a label not granted to the user
+   * will fail the entire modify operation early. If false the operation will
+   * execute anyway, but the proposed labels given by the user will be
+   * modified to be the "best" value allowed by the access controls, or
+   * ignored if the label does not exist.
+   */
+  public boolean strictLabels = true;
+
+  /**
+   * How to process draft comments already in the database that were not also
+   * described in this input request.
+   */
+  public DraftHandling drafts = DraftHandling.DELETE;
+
+  /** Who to send email notifications to after review is stored. */
+  public NotifyHandling notify = NotifyHandling.ALL;
+
+  /**
+   * Account ID, name, email address or username of another user. The review
+   * will be posted/updated on behalf of this named user instead of the
+   * caller. Caller must have the labelAs-$NAME permission granted for each
+   * label that appears in {@link #labels}. This is in addition to the named
+   * user also needing to have permission to use the labels.
+   * <p>
+   * {@link #strictLabels} impacts how labels is processed for the named user,
+   * not the caller.
+   */
+  public String onBehalfOf;
+
+  public static enum DraftHandling {
+    DELETE, PUBLISH, KEEP
+  }
+
+  public static enum NotifyHandling {
+    NONE, OWNER, OWNER_REVIEWERS, ALL
+  }
+
+  public static enum Side {
+    PARENT, REVISION
+  }
+
+  public static class Comment {
+    public String id;
+    public Side side;
+    public int line;
+    public String inReplyTo;
+    public String message;
+    public Range range;
+
+    public static class Range {
+      public int startLine;
+      public int startCharacter;
+      public int endLine;
+      public int endCharacter;
+    }
+  }
+
+  public ReviewInput message(String msg) {
+    message = msg != null && !msg.isEmpty() ? msg : null;
+    return this;
+  }
+
+  public ReviewInput label(String name, short value) {
+    if (name == null || name.isEmpty()) {
+      throw new IllegalArgumentException();
+    }
+    if (labels == null) {
+      labels = new LinkedHashMap<String, Short>(4);
+    }
+    labels.put(name, value);
+    return this;
+  }
+
+  public ReviewInput label(String name, int value) {
+    if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
+      throw new IllegalArgumentException();
+    }
+    return label(name, (short) value);
+  }
+
+  public ReviewInput label(String name) {
+    return label(name, (short) 1);
+  }
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.java
new file mode 100644
index 0000000..ff4a94e
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.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.api.changes;
+
+import com.google.gerrit.extensions.restapi.RestApiException;
+
+public interface RevisionApi {
+  void delete() throws RestApiException;
+  void rebase() throws RestApiException;
+  void review(ReviewInput in) throws RestApiException;
+
+  /** {@code submit} with {@link SubmitInput#waitForMerge} set to true. */
+  void submit() throws RestApiException;
+  void submit(SubmitInput in) throws RestApiException;
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/SubmitInput.java
similarity index 63%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/SubmitInput.java
index a5371d2..91e3c70 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/SubmitInput.java
@@ -12,19 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.extensions.api.changes;
 
-import com.google.common.collect.Maps;
-
-import java.util.Map;
-
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
-  }
+public class SubmitInput {
+  public boolean waitForMerge;
 }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/config/DownloadScheme.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/config/DownloadScheme.java
index 20eda97..d81657a 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/config/DownloadScheme.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/config/DownloadScheme.java
@@ -30,9 +30,7 @@
   public abstract boolean isAuthRequired();
 
   /** @return whether this scheme supports authentication */
-  public boolean isAuthSupported() {
-    return isAuthRequired();
-  }
+  public abstract boolean isAuthSupported();
 
   /** @return whether the download scheme is enabled */
   public abstract boolean isEnabled();
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/CacheControl.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/CacheControl.java
index 72f1bc5..3aa1781 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/CacheControl.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/CacheControl.java
@@ -19,7 +19,7 @@
 public class CacheControl {
 
   public enum Type {
-    NONE, PUBLIC, PRIVATE;
+    NONE, PUBLIC, PRIVATE
   }
 
   public static final CacheControl NONE = new CacheControl(Type.NONE, 0, null);
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestApiException.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestApiException.java
index 3fae128..b6ba730 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestApiException.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestApiException.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.extensions.restapi;
 
 /** Root exception type for JSON API failures. */
-public abstract class RestApiException extends Exception {
+public class RestApiException extends Exception {
   private static final long serialVersionUID = 1L;
   private CacheControl caching = CacheControl.NONE;
 
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/systemstatus/ServerInformation.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/systemstatus/ServerInformation.java
index 3d2df21..c7deedb 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/systemstatus/ServerInformation.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/systemstatus/ServerInformation.java
@@ -35,7 +35,7 @@
      * The server is attempting a graceful halt of operations and will exit (or
      * be killed by the operating system) soon.
      */
-    SHUTDOWN;
+    SHUTDOWN
   }
 
   State getState();
diff --git a/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritDebugLauncher.java b/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritDebugLauncher.java
index a2a770e..5770f58 100644
--- a/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritDebugLauncher.java
+++ b/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritDebugLauncher.java
@@ -368,7 +368,7 @@
 
     AbstractConnector connector = getConnector();
     if (bindAddress != null) {
-      connector.setHost(bindAddress.toString());
+      connector.setHost(bindAddress);
     }
     connector.setPort(port);
 
diff --git a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/progress/client/ProgressBar.java b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/progress/client/ProgressBar.java
index 5e13f55..931e84e 100644
--- a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/progress/client/ProgressBar.java
+++ b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/progress/client/ProgressBar.java
@@ -71,7 +71,7 @@
   public void setValue(final int pComplete) {
     assert 0 <= pComplete && pComplete <= 100;
     value = pComplete;
-    bar.setWidth("" + (2 * pComplete) + "px");
+    bar.setWidth(2 * pComplete + "px");
     msg.setText(callerText + pComplete + "%");
   }
 }
diff --git a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/LinkFindReplaceTest.java b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/LinkFindReplaceTest.java
index 97f816f..0d8336e 100644
--- a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/LinkFindReplaceTest.java
+++ b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/LinkFindReplaceTest.java
@@ -15,10 +15,15 @@
 package com.google.gwtexpui.safehtml.client;
 
 import static com.google.gwtexpui.safehtml.client.LinkFindReplace.hasValidScheme;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
-public class LinkFindReplaceTest extends TestCase {
+public class LinkFindReplaceTest {
+  @Test
   public void testNoEscaping() {
     String find = "find";
     String link = "link";
@@ -28,12 +33,14 @@
     assertEquals("find = " + find + ", link = " + link, a.toString());
   }
 
+  @Test
   public void testBackreference() {
     assertEquals("<a href=\"/bug?id=123\">issue 123</a>",
         new LinkFindReplace("(bug|issue)\\s*([0-9]+)", "/bug?id=$2")
             .replace("issue 123"));
   }
 
+  @Test
   public void testHasValidScheme() {
     assertTrue(hasValidScheme("/absolute/path"));
     assertTrue(hasValidScheme("relative/path"));
@@ -46,6 +53,7 @@
     assertFalse(hasValidScheme("javascript:alert(1)"));
   }
 
+  @Test
   public void testInvalidSchemeInReplace() {
     try {
       new LinkFindReplace("find", "javascript:alert(1)").replace("find");
@@ -54,6 +62,7 @@
     }
   }
 
+  @Test
   public void testInvalidSchemeWithBackreference() {
     try {
       new LinkFindReplace(".*(script:[^;]*)", "java$1")
@@ -63,11 +72,13 @@
     }
   }
 
+  @Test
   public void testReplaceEscaping() {
     assertEquals("<a href=\"a&quot;&amp;&#39;&lt;&gt;b\">find</a>",
         new LinkFindReplace("find", "a\"&'<>b").replace("find"));
   }
 
+  @Test
   public void testHtmlInFind() {
     String rawFind = "<b>&quot;bold&quot;</b>";
     LinkFindReplace a = new LinkFindReplace(rawFind, "/bold");
diff --git a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/RawFindReplaceTest.java b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/RawFindReplaceTest.java
index 9c450bd..bc20a9d 100644
--- a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/RawFindReplaceTest.java
+++ b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/RawFindReplaceTest.java
@@ -14,9 +14,11 @@
 
 package com.google.gwtexpui.safehtml.client;
 
-import junit.framework.TestCase;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
 
-public class RawFindReplaceTest extends TestCase {
+public class RawFindReplaceTest {
+  @Test
   public void testFindReplace() {
     final String find = "find";
     final String replace = "replace";
diff --git a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtmlBuilderTest.java b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtmlBuilderTest.java
index a6b0012..0163d7f 100644
--- a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtmlBuilderTest.java
+++ b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtmlBuilderTest.java
@@ -14,9 +14,18 @@
 
 package com.google.gwtexpui.safehtml.client;
 
-import junit.framework.TestCase;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
-public class SafeHtmlBuilderTest extends TestCase {
+import org.junit.Test;
+
+public class SafeHtmlBuilderTest {
+  @Test
   public void testEmpty() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertTrue(b.isEmpty());
@@ -28,6 +37,7 @@
     assertEquals("a", b.asString());
   }
 
+  @Test
   public void testToSafeHtml() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     b.append(1);
@@ -39,6 +49,7 @@
     assertEquals("1", h.asString());
   }
 
+  @Test
   public void testAppend_boolean() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.append(true));
@@ -46,6 +57,7 @@
     assertEquals("truefalse", b.asString());
   }
 
+  @Test
   public void testAppend_char() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.append('a'));
@@ -53,6 +65,7 @@
     assertEquals("ab", b.asString());
   }
 
+  @Test
   public void testAppend_int() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.append(4));
@@ -61,6 +74,7 @@
     assertEquals("42-100", b.asString());
   }
 
+  @Test
   public void testAppend_long() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.append(4L));
@@ -68,18 +82,21 @@
     assertEquals("42", b.asString());
   }
 
+  @Test
   public void testAppend_float() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.append(0.0f));
     assertEquals("0.0", b.asString());
   }
 
+  @Test
   public void testAppend_double() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.append(0.0));
     assertEquals("0.0", b.asString());
   }
 
+  @Test
   public void testAppend_String() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.append((String) null));
@@ -89,6 +106,7 @@
     assertEquals("foobar", b.asString());
   }
 
+  @Test
   public void testAppend_StringBuilder() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.append((StringBuilder) null));
@@ -98,6 +116,7 @@
     assertEquals("foobar", b.asString());
   }
 
+  @Test
   public void testAppend_StringBuffer() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.append((StringBuffer) null));
@@ -107,6 +126,7 @@
     assertEquals("foobar", b.asString());
   }
 
+  @Test
   public void testAppend_Object() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.append((Object) null));
@@ -120,6 +140,7 @@
     assertEquals("foobar", b.asString());
   }
 
+  @Test
   public void testAppend_CharSequence() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.append((CharSequence) null));
@@ -129,6 +150,7 @@
     assertEquals("foobar", b.asString());
   }
 
+  @Test
   public void testAppend_SafeHtml() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.append((SafeHtml) null));
@@ -138,6 +160,7 @@
     assertEquals("foobar", b.asString());
   }
 
+  @Test
   public void testHtmlSpecialCharacters() {
     assertEquals("&amp;", escape("&"));
     assertEquals("&lt;", escape("<"));
@@ -155,18 +178,21 @@
     assertEquals("&amp;lt;b&amp;gt;", escape("&lt;b&gt;"));
   }
 
+  @Test
   public void testEntityNbsp() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.nbsp());
     assertEquals("&nbsp;", b.asString());
   }
 
+  @Test
   public void testTagBr() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.br());
     assertEquals("<br />", b.asString());
   }
 
+  @Test
   public void testTagTableTrTd() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.openElement("table"));
@@ -179,6 +205,7 @@
     assertEquals("<table><tr><td>d&lt;a&gt;ta</td></tr></table>", b.asString());
   }
 
+  @Test
   public void testTagDiv() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.openDiv());
@@ -187,6 +214,7 @@
     assertEquals("<div>d&lt;a&gt;ta</div>", b.asString());
   }
 
+  @Test
   public void testTagAnchor() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.openAnchor());
@@ -206,6 +234,7 @@
     assertEquals("<a href=\"d&lt;a&gt;ta\">go</a>", b.asString());
   }
 
+  @Test
   public void testTagHeightWidth() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.openElement("img"));
@@ -215,6 +244,7 @@
     assertEquals("<img height=\"100\" width=\"42\" />", b.asString());
   }
 
+  @Test
   public void testStyleName() {
     final SafeHtmlBuilder b = new SafeHtmlBuilder();
     assertSame(b, b.openSpan());
@@ -225,6 +255,7 @@
     assertEquals("<span class=\"foo bar\">d&lt;a&gt;ta</span>", b.asString());
   }
 
+  @Test
   public void testRejectJavaScript_AnchorHref() {
     final String href = "javascript:window.close();";
     try {
@@ -235,6 +266,7 @@
     }
   }
 
+  @Test
   public void testRejectJavaScript_ImgSrc() {
     final String href = "javascript:window.close();";
     try {
@@ -245,6 +277,7 @@
     }
   }
 
+  @Test
   public void testRejectJavaScript_FormAction() {
     final String href = "javascript:window.close();";
     try {
diff --git a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_LinkifyTest.java b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_LinkifyTest.java
index a9d9450..749df17 100644
--- a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_LinkifyTest.java
+++ b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_LinkifyTest.java
@@ -14,9 +14,13 @@
 
 package com.google.gwtexpui.safehtml.client;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
-public class SafeHtml_LinkifyTest extends TestCase {
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+
+public class SafeHtml_LinkifyTest {
+  @Test
   public void testLinkify_SimpleHttp1() {
     final SafeHtml o = html("A http://go.here/ B");
     final SafeHtml n = o.linkify();
@@ -24,6 +28,7 @@
     assertEquals("A <a href=\"http://go.here/\" target=\"_blank\">http://go.here/</a> B", n.asString());
   }
 
+  @Test
   public void testLinkify_SimpleHttps2() {
     final SafeHtml o = html("A https://go.here/ B");
     final SafeHtml n = o.linkify();
@@ -31,6 +36,7 @@
     assertEquals("A <a href=\"https://go.here/\" target=\"_blank\">https://go.here/</a> B", n.asString());
   }
 
+  @Test
   public void testLinkify_Parens1() {
     final SafeHtml o = html("A (http://go.here/) B");
     final SafeHtml n = o.linkify();
@@ -38,6 +44,7 @@
     assertEquals("A (<a href=\"http://go.here/\" target=\"_blank\">http://go.here/</a>) B", n.asString());
   }
 
+  @Test
   public void testLinkify_Parens() {
     final SafeHtml o = html("A http://go.here/#m() B");
     final SafeHtml n = o.linkify();
@@ -45,6 +52,7 @@
     assertEquals("A <a href=\"http://go.here/#m()\" target=\"_blank\">http://go.here/#m()</a> B", n.asString());
   }
 
+  @Test
   public void testLinkify_AngleBrackets1() {
     final SafeHtml o = html("A <http://go.here/> B");
     final SafeHtml n = o.linkify();
diff --git a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_ReplaceTest.java b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_ReplaceTest.java
index d7a3aaf..71b55a1 100644
--- a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_ReplaceTest.java
+++ b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_ReplaceTest.java
@@ -14,19 +14,25 @@
 
 package com.google.gwtexpui.safehtml.client;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-public class SafeHtml_ReplaceTest extends TestCase {
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+
+public class SafeHtml_ReplaceTest {
+  @Test
   public void testReplaceEmpty() {
     SafeHtml o = html("A\nissue42\nB");
     assertSame(o, o.replaceAll(null));
     assertSame(o, o.replaceAll(Collections.<FindReplace> emptyList()));
   }
 
+  @Test
   public void testReplaceOneLink() {
     SafeHtml o = html("A\nissue 42\nB");
     SafeHtml n = o.replaceAll(repls(
@@ -35,6 +41,7 @@
     assertEquals("A\n<a href=\"?42\">issue 42</a>\nB", n.asString());
   }
 
+  @Test
   public void testReplaceNoLeadingOrTrailingText() {
     SafeHtml o = html("issue 42");
     SafeHtml n = o.replaceAll(repls(
@@ -43,6 +50,7 @@
     assertEquals("<a href=\"?42\">issue 42</a>", n.asString());
   }
 
+  @Test
   public void testReplaceTwoLinks() {
     SafeHtml o = html("A\nissue 42\nissue 9918\nB");
     SafeHtml n = o.replaceAll(repls(
@@ -55,6 +63,7 @@
     , n.asString());
   }
 
+  @Test
   public void testReplaceInOrder() {
     SafeHtml o = html("A\nissue 42\nReally GWTEXPUI-9918 is better\nB");
     SafeHtml n = o.replaceAll(repls(
@@ -70,6 +79,7 @@
     , n.asString());
   }
 
+  @Test
   public void testReplaceOverlappingAfterFirstChar() {
     SafeHtml o = html("abcd");
     RawFindReplace ab = new RawFindReplace("ab", "AB");
@@ -81,6 +91,7 @@
     assertEquals("ABYZ", o.replaceAll(repls(ab, bc, cd)).asString());
   }
 
+  @Test
   public void testReplaceOverlappingAtFirstCharLongestMatch() {
     SafeHtml o = html("abcd");
     RawFindReplace ab = new RawFindReplace("ab", "AB");
@@ -90,6 +101,7 @@
     assertEquals("234d", o.replaceAll(repls(abc, ab)).asString());
   }
 
+  @Test
   public void testReplaceOverlappingAtFirstCharFirstMatch() {
     SafeHtml o = html("abcd");
     RawFindReplace ab1 = new RawFindReplace("ab", "AB");
@@ -99,6 +111,7 @@
     assertEquals("12cd", o.replaceAll(repls(ab2, ab1)).asString());
   }
 
+  @Test
   public void testFailedSanitization() {
     SafeHtml o = html("abcd");
     LinkFindReplace evil = new LinkFindReplace("(b)", "javascript:alert('$1')");
diff --git a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyListTest.java b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyListTest.java
index 250a1b5..045555a 100644
--- a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyListTest.java
+++ b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyListTest.java
@@ -14,9 +14,12 @@
 
 package com.google.gwtexpui.safehtml.client;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
-public class SafeHtml_WikifyListTest extends TestCase {
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+
+public class SafeHtml_WikifyListTest {
   private static final String BEGIN_LIST = "<ul class=\"wikiList\">";
   private static final String END_LIST = "</ul>";
 
@@ -24,6 +27,7 @@
     return "<li>" + raw + "</li>";
   }
 
+  @Test
   public void testBulletList1() {
     final SafeHtml o = html("A\n\n* line 1\n* 2nd line");
     final SafeHtml n = o.wikify();
@@ -36,6 +40,7 @@
     , n.asString());
   }
 
+  @Test
   public void testBulletList2() {
     final SafeHtml o = html("A\n\n* line 1\n* 2nd line\n\nB");
     final SafeHtml n = o.wikify();
@@ -49,6 +54,7 @@
     , n.asString());
   }
 
+  @Test
   public void testBulletList3() {
     final SafeHtml o = html("* line 1\n* 2nd line\n\nB");
     final SafeHtml n = o.wikify();
@@ -61,6 +67,7 @@
     , n.asString());
   }
 
+  @Test
   public void testBulletList4() {
     final SafeHtml o = html("To see this bug, you have to:\n" //
         + "* Be on IMAP or EAS (not on POP)\n"//
@@ -75,6 +82,7 @@
     , n.asString());
   }
 
+  @Test
   public void testBulletList5() {
     final SafeHtml o = html("To see this bug,\n" //
         + "you have to:\n" //
@@ -90,6 +98,7 @@
     , n.asString());
   }
 
+  @Test
   public void testDashList1() {
     final SafeHtml o = html("A\n\n- line 1\n- 2nd line");
     final SafeHtml n = o.wikify();
@@ -102,6 +111,7 @@
     , n.asString());
   }
 
+  @Test
   public void testDashList2() {
     final SafeHtml o = html("A\n\n- line 1\n- 2nd line\n\nB");
     final SafeHtml n = o.wikify();
@@ -115,6 +125,7 @@
     , n.asString());
   }
 
+  @Test
   public void testDashList3() {
     final SafeHtml o = html("- line 1\n- 2nd line\n\nB");
     final SafeHtml n = o.wikify();
diff --git a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyPreformatTest.java b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyPreformatTest.java
index cbb315b..605185e 100644
--- a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyPreformatTest.java
+++ b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyPreformatTest.java
@@ -14,9 +14,12 @@
 
 package com.google.gwtexpui.safehtml.client;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
-public class SafeHtml_WikifyPreformatTest extends TestCase {
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+
+public class SafeHtml_WikifyPreformatTest {
   private static final String B = "<span class=\"wikiPreFormat\">";
   private static final String E = "</span><br />";
 
@@ -24,6 +27,7 @@
     return B + raw + E;
   }
 
+  @Test
   public void testPreformat1() {
     final SafeHtml o = html("A\n\n  This is pre\n  formatted");
     final SafeHtml n = o.wikify();
@@ -36,6 +40,7 @@
     , n.asString());
   }
 
+  @Test
   public void testPreformat2() {
     final SafeHtml o = html("A\n\n  This is pre\n  formatted\n\nbut this is not");
     final SafeHtml n = o.wikify();
@@ -49,6 +54,7 @@
     , n.asString());
   }
 
+  @Test
   public void testPreformat3() {
     final SafeHtml o = html("A\n\n  Q\n    <R>\n  S\n\nB");
     final SafeHtml n = o.wikify();
@@ -63,6 +69,7 @@
     , n.asString());
   }
 
+  @Test
   public void testPreformat4() {
     final SafeHtml o = html("  Q\n    <R>\n  S\n\nB");
     final SafeHtml n = o.wikify();
diff --git a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyTest.java b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyTest.java
index c9837037..00b29de 100644
--- a/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyTest.java
+++ b/gerrit-gwtexpui/src/test/java/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyTest.java
@@ -14,9 +14,13 @@
 
 package com.google.gwtexpui.safehtml.client;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
-public class SafeHtml_WikifyTest extends TestCase {
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+
+public class SafeHtml_WikifyTest {
+  @Test
   public void testWikify_OneLine1() {
     final SafeHtml o = html("A  B");
     final SafeHtml n = o.wikify();
@@ -24,6 +28,7 @@
     assertEquals("<p>A  B</p>", n.asString());
   }
 
+  @Test
   public void testWikify_OneLine2() {
     final SafeHtml o = html("A  B\n");
     final SafeHtml n = o.wikify();
@@ -31,6 +36,7 @@
     assertEquals("<p>A  B\n</p>", n.asString());
   }
 
+  @Test
   public void testWikify_OneParagraph1() {
     final SafeHtml o = html("A\nB");
     final SafeHtml n = o.wikify();
@@ -38,6 +44,7 @@
     assertEquals("<p>A\nB</p>", n.asString());
   }
 
+  @Test
   public void testWikify_OneParagraph2() {
     final SafeHtml o = html("A\nB\n");
     final SafeHtml n = o.wikify();
@@ -45,6 +52,7 @@
     assertEquals("<p>A\nB\n</p>", n.asString());
   }
 
+  @Test
   public void testWikify_TwoParagraphs() {
     final SafeHtml o = html("A\nB\n\nC\nD");
     final SafeHtml n = o.wikify();
@@ -52,6 +60,7 @@
     assertEquals("<p>A\nB</p><p>C\nD</p>", n.asString());
   }
 
+  @Test
   public void testLinkify_SimpleHttp1() {
     final SafeHtml o = html("A http://go.here/ B");
     final SafeHtml n = o.wikify();
@@ -59,6 +68,7 @@
     assertEquals("<p>A <a href=\"http://go.here/\" target=\"_blank\">http://go.here/</a> B</p>", n.asString());
   }
 
+  @Test
   public void testLinkify_SimpleHttps2() {
     final SafeHtml o = html("A https://go.here/ B");
     final SafeHtml n = o.wikify();
@@ -66,6 +76,7 @@
     assertEquals("<p>A <a href=\"https://go.here/\" target=\"_blank\">https://go.here/</a> B</p>", n.asString());
   }
 
+  @Test
   public void testLinkify_Parens1() {
     final SafeHtml o = html("A (http://go.here/) B");
     final SafeHtml n = o.wikify();
@@ -73,6 +84,7 @@
     assertEquals("<p>A (<a href=\"http://go.here/\" target=\"_blank\">http://go.here/</a>) B</p>", n.asString());
   }
 
+  @Test
   public void testLinkify_Parens() {
     final SafeHtml o = html("A http://go.here/#m() B");
     final SafeHtml n = o.wikify();
@@ -80,6 +92,7 @@
     assertEquals("<p>A <a href=\"http://go.here/#m()\" target=\"_blank\">http://go.here/#m()</a> B</p>", n.asString());
   }
 
+  @Test
   public void testLinkify_AngleBrackets1() {
     final SafeHtml o = html("A <http://go.here/> B");
     final SafeHtml n = o.wikify();
diff --git a/gerrit-gwtui/BUCK b/gerrit-gwtui/BUCK
index 1ae823e..0c873ee 100644
--- a/gerrit-gwtui/BUCK
+++ b/gerrit-gwtui/BUCK
@@ -1,4 +1,5 @@
 include_defs('//gerrit-gwtui/gwt.defs')
+include_defs('//tools/gwt-constants.defs')
 
 genrule(
   name = 'ui_optdbg',
@@ -25,13 +26,7 @@
 gwt_application(
   name = 'ui_opt',
   module_target = MODULE,
-  compiler_opts = [
-    '-strict',
-    '-style', 'OBF',
-    '-optimize', '9',
-    '-XdisableClassMetadata',
-    '-XdisableCastChecking',
-  ],
+  compiler_opts = GWT_COMPILER_OPTS,
   deps = APP_DEPS,
 )
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
index 40e5252..7b5389e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
@@ -71,6 +71,7 @@
 import com.google.gerrit.client.dashboards.DashboardInfo;
 import com.google.gerrit.client.dashboards.DashboardList;
 import com.google.gerrit.client.diff.SideBySide2;
+import com.google.gerrit.client.documentation.DocScreen;
 import com.google.gerrit.client.groups.GroupApi;
 import com.google.gerrit.client.groups.GroupInfo;
 import com.google.gerrit.client.patches.PatchScreen;
@@ -201,6 +202,9 @@
     if (matchPrefix("/q/", token)) {
       query(token);
 
+    } else if (matchPrefix("/Documentation/", token)) {
+      docSearch(token);
+
     } else if (matchPrefix("/c/", token)) {
       change(token);
 
@@ -884,4 +888,12 @@
       }
     }
   }
+
+  private static void docSearch(final String token) {
+    GWT.runAsync(new AsyncSplit(token) {
+      public void onSuccess() {
+        Gerrit.display(token, new DocScreen(skip(token)));
+      }
+    });
+  }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java
index cbb5513..21da8ce 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java
@@ -34,4 +34,6 @@
 
   String pluginFailed(String scriptPath);
   String cannotDownloadPlugin(String scriptPath);
+
+  String parentUpdateFailed(String message);
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties
index 5ab8b3b..8196e98 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties
@@ -15,3 +15,5 @@
 
 pluginFailed = Plugin JavaScript {0} failed to load
 cannotDownloadPlugin = Cannot download JavaScript plugin from: {0}.
+
+parentUpdateFailed = Setting parent project failed: {0}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritResources.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritResources.java
index 80d65b0..dffa7c4 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritResources.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritResources.java
@@ -57,4 +57,7 @@
 
   @Source("draftComments.png")
   public ImageResource draftComments();
+
+  @Source("readOnly.png")
+  public ImageResource readOnly();
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java
index da9f1c6..4c69300 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java
@@ -22,13 +22,13 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 import java.util.TreeSet;
 
 public class SearchSuggestOracle extends HighlightSuggestOracle {
   private static final List<ParamSuggester> paramSuggester = Arrays.asList(
-      new ParamSuggester("project:", new ProjectNameSuggestOracle()),
+      new ParamSuggester(Arrays.asList("project:", "parentproject:"),
+          new ProjectNameSuggestOracle()),
       new ParamSuggester(Arrays.asList("owner:", "reviewer:"),
           new AccountSuggestOracle() {
             @Override
@@ -80,7 +80,9 @@
 
     suggestions.add("commit:");
     suggestions.add("comment:");
+    suggestions.add("conflicts:");
     suggestions.add("project:");
+    suggestions.add("parentproject:");
     suggestions.add("branch:");
     suggestions.add("topic:");
     suggestions.add("ref:");
@@ -89,7 +91,6 @@
     suggestions.add("label:");
     suggestions.add("message:");
     suggestions.add("file:");
-
     suggestions.add("has:");
     suggestions.add("has:draft");
     suggestions.add("has:star");
@@ -207,11 +208,6 @@
     private final List<String> operators;
     private final SuggestOracle parameterSuggestionOracle;
 
-    ParamSuggester(final String operator,
-        final SuggestOracle parameterSuggestionOracle) {
-      this(Collections.singletonList(operator), parameterSuggestionOracle);
-    }
-
     ParamSuggester(final List<String> operators,
         final SuggestOracle parameterSuggestionOracle) {
       this.operators = operators;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
index 333dcfa..0aeeac6 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
@@ -77,14 +77,14 @@
       Confirm the default path <em>.ssh/id_rsa</em>\
     </li>\
     <li>\
-      Enter a passphrase (recommended) or leave it blank.<br>\
-      Remember this passphrase, as you will need it to unlock the<br>\
+      Enter a passphrase (recommended) or leave it blank.<br />\
+      Remember this passphrase, as you will need it to unlock the<br />\
       key whenever you use it.\
     </li>\
     <li>\
-      Open <em>~/.ssh/id_rsa.pub</em> and copy & paste the contents into<br>\
-      the box below, then click on "Add".<br>\
-      Note that <em>id_rsa.pub</em> is your public key and can be shared,<br>\
+      Open <em>~/.ssh/id_rsa.pub</em> and copy & paste the contents into<br />\
+      the box below, then click on "Add".<br />\
+      Note that <em>id_rsa.pub</em> is your public key and can be shared,<br />\
       while <em>id_rsa</em> is your private key and should be kept secret.\
     </li>\
   <\ol>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
index 731eaeb..2dd4df7 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
@@ -280,7 +280,7 @@
         return;
       }
     }
-    if (currentValue != defaultValue) {
+    if (!currentValue.equals(defaultValue)) {
       setListBox(f, defaultValue, defaultValue);
     }
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java
index 49a9aa4..567f14c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.client.GitwebLink;
 import com.google.gerrit.client.ui.Hyperlink;
+import com.google.gerrit.client.ui.ParentProjectBox;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.ProjectAccess;
 import com.google.gerrit.reviewdb.client.Branch;
@@ -56,6 +57,10 @@
   Hyperlink parentProject;
 
   @UiField
+  @Editor.Ignore
+  ParentProjectBox parentProjectBox;
+
+  @UiField
   DivElement history;
 
   @UiField
@@ -106,6 +111,11 @@
       parentProject.setText(parent.get());
       parentProject.setTargetHistoryToken( //
           Dispatcher.toProjectAdmin(parent, ProjectScreen.ACCESS));
+
+      parentProjectBox.setVisible(editing && value.canChangeParent());
+      parentProjectBox.setProject(value.getProjectName());
+      parentProjectBox.setParentProject(value.getInheritsFrom());
+      parentProject.setVisible(!parentProjectBox.isVisible());
     } else {
       inheritsFrom.getStyle().setDisplay(Display.NONE);
     }
@@ -135,6 +145,7 @@
       }
     }
     value.setLocal(keep);
+    value.setInheritsFrom(parentProjectBox.getParentProjectName());
   }
 
   @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml
index 4942b83..120824b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml
@@ -56,6 +56,9 @@
   <div ui:field='inheritsFrom' class='{style.inheritsFrom}'>
     <span class='{style.parentTitle}'><ui:msg>Rights Inherit From:</ui:msg></span>
     <q:Hyperlink ui:field='parentProject' styleName='{style.parentLink}'/>
+    <q:ParentProjectBox
+      ui:field='parentProjectBox'
+      visible='false'/>
   </div>
   <div ui:field='history' class='{style.history}'>
     <span class='{style.historyTitle}'><ui:msg>History:</ui:msg></span>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java
index ef7f560..c76bba9 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java
@@ -17,6 +17,7 @@
 import static com.google.gerrit.common.ProjectAccessUtil.mergeSections;
 import static com.google.gerrit.common.ProjectAccessUtil.removeEmptyPermissionsAndSections;
 
+import com.google.gerrit.client.ErrorDialog;
 import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.client.config.CapabilityInfo;
 import com.google.gerrit.client.config.ConfigServerApi;
@@ -28,6 +29,7 @@
 import com.google.gerrit.common.PageLinks;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.ProjectAccess;
+import com.google.gerrit.common.errors.UpdateParentFailedException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gwt.core.client.GWT;
@@ -45,6 +47,7 @@
 import com.google.gwt.user.client.ui.UIObject;
 import com.google.gwt.user.client.ui.VerticalPanel;
 import com.google.gwtexpui.globalkey.client.NpTextArea;
+import com.google.gwtjsonrpc.client.RemoteJsonException;
 
 import java.util.Collections;
 import java.util.HashMap;
@@ -205,6 +208,7 @@
         access.getRevision(), //
         message, //
         access.getLocal(), //
+        access.getInheritsFrom(), //
         new GerritCallback<ProjectAccess>() {
           @Override
           public void onSuccess(ProjectAccess newAccess) {
@@ -250,7 +254,15 @@
           public void onFailure(Throwable caught) {
             error.clear();
             enable(true);
-            super.onFailure(caught);
+            if (caught instanceof RemoteJsonException
+                && caught.getMessage().startsWith(
+                    UpdateParentFailedException.MESSAGE)) {
+              new ErrorDialog(Gerrit.M.parentUpdateFailed(caught.getMessage()
+                  .substring(UpdateParentFailedException.MESSAGE.length() + 1)))
+                  .center();
+            } else {
+              super.onFailure(caught);
+            }
           }
         });
   }
@@ -275,6 +287,7 @@
         access.getRevision(), //
         message, //
         access.getLocal(), //
+        access.getInheritsFrom(), //
         new GerritCallback<Change.Id>() {
           @Override
           public void onSuccess(Change.Id changeId) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
index 331afee..23e13d4 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
@@ -37,6 +37,7 @@
 import com.google.gwt.user.client.ui.Anchor;
 import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Image;
 import com.google.gwt.user.client.ui.Label;
 import com.google.gwtexpui.globalkey.client.NpTextBox;
 
@@ -102,9 +103,11 @@
       protected void initColumnHeaders() {
         super.initColumnHeaders();
         if (Gerrit.getGitwebLink() != null) {
-          table.setText(0, 3, Util.C.projectRepoBrowser());
+          table.setText(0, ProjectsTable.C_REPO_BROWSER,
+              Util.C.projectRepoBrowser());
           table.getFlexCellFormatter().
-            addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
+            addStyleName(0, ProjectsTable.C_REPO_BROWSER,
+                Gerrit.RESOURCES.css().dataHeader());
         }
       }
 
@@ -122,20 +125,39 @@
         super.insert(row, k);
         if (Gerrit.getGitwebLink() != null) {
           table.getFlexCellFormatter().
-            addStyleName(row, 3, Gerrit.RESOURCES.css().dataCell());
+            addStyleName(row, ProjectsTable.C_REPO_BROWSER,
+                Gerrit.RESOURCES.css().dataCell());
         }
       }
 
       @Override
       protected void populate(final int row, final ProjectInfo k) {
+        Image state = new Image();
+        switch (k.state()) {
+          case HIDDEN:
+            state.setResource(Gerrit.RESOURCES.redNot());
+            state.setTitle(Util.toLongString(k.state()));
+            table.setWidget(row, ProjectsTable.C_STATE, state);
+            break;
+          case READ_ONLY:
+            state.setResource(Gerrit.RESOURCES.readOnly());
+            state.setTitle(Util.toLongString(k.state()));
+            table.setWidget(row, ProjectsTable.C_STATE, state);
+            break;
+          default:
+            // Intentionally left blank, do not show an icon when active.
+            break;
+        }
+
         FlowPanel fp = new FlowPanel();
         fp.add(new ProjectSearchLink(k.name_key()));
         fp.add(new HighlightingInlineHyperlink(k.name(), link(k), subname));
-        table.setWidget(row, 1, fp);
-        table.setText(row, 2, k.description());
+        table.setWidget(row, ProjectsTable.C_NAME, fp);
+        table.setText(row, ProjectsTable.C_DESCRIPTION, k.description());
         GitwebLink l = Gerrit.getGitwebLink();
         if (l != null) {
-          table.setWidget(row, 3, new Anchor(l.getLinkName(), false, l.toProject(k
+          table.setWidget(row, ProjectsTable.C_REPO_BROWSER,
+              new Anchor(l.getLinkName(), false, l.toProject(k
               .name_key())));
         }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteHover.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteHover.png
index 839e8ef..9fde3fa 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteHover.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteHover.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteNormal.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteNormal.png
index ffddb6f..47a1195 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteNormal.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteNormal.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/editText.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/editText.png
index 188e1c1..2927275 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/editText.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/editText.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/undoNormal.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/undoNormal.png
index 8b0fef9..b780f75 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/undoNormal.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/undoNormal.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java
index 60377c4..f22fa3f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java
@@ -93,6 +93,21 @@
         e.onkeypress = stopPropagation;
         return e;
       },
+      select: function(a,s) {
+        var e = doc.createElement('select');
+        for (var i = 0; i < a.length; i++) {
+          var o = doc.createElement('option');
+          if (i==s) {
+            o.setAttributeNode(doc.createAttribute("selected"));
+          }
+          o.appendChild(doc.createTextNode(a[i]));
+          e.appendChild(o);
+        }
+        return e;
+      },
+      selected: function(e) {
+        return e.options[e.selectedIndex].text;
+      },
 
       popup: function(e){this._p=@com.google.gerrit.client.api.PopupHelper::popup(Lcom/google/gerrit/client/api/ActionContext;Lcom/google/gwt/dom/client/Element;)(this,e)},
       hide: function() {
@@ -135,10 +150,30 @@
   }
 
   static final void post(RestApi api, JavaScriptObject in, JavaScriptObject cb) {
+    if (NativeString.is(in)) {
+      post(api, ((NativeString) in).asString(), cb);
+    } else {
+      api.post(in, wrap(cb));
+    }
+  }
+
+  static final void post(RestApi api, String in, JavaScriptObject cb) {
     api.post(in, wrap(cb));
   }
 
+  static final void put(RestApi api, JavaScriptObject cb) {
+    api.put(wrap(cb));
+  }
+
   static final void put(RestApi api, JavaScriptObject in, JavaScriptObject cb) {
+    if (NativeString.is(in)) {
+      put(api, ((NativeString) in).asString(), cb);
+    } else {
+      api.put(in, wrap(cb));
+    }
+  }
+
+  static final void put(RestApi api, String in, JavaScriptObject cb) {
     api.put(in, wrap(cb));
   }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ApiGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ApiGlue.java
index f3fb1fa..1e2e19e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ApiGlue.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ApiGlue.java
@@ -59,9 +59,24 @@
 
       _api: function(u) {return @com.google.gerrit.client.rpc.RestApi::new(Ljava/lang/String;)(u)},
       get: function(u,b){@com.google.gerrit.client.api.ActionContext::get(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),b)},
-      post: function(u,i,b){@com.google.gerrit.client.api.ActionContext::post(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),i,b)},
-      put: function(u,i,b){@com.google.gerrit.client.api.ActionContext::put(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),i,b)},
+      post: function(u,i,b){
+        if (typeof i=='string')
+          @com.google.gerrit.client.api.ActionContext::post(Lcom/google/gerrit/client/rpc/RestApi;Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),i,b);
+        else
+          @com.google.gerrit.client.api.ActionContext::post(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),i,b);
+      },
+      put: function(u,i,b){
+        if(b){
+          if(typeof i=='string')
+            @com.google.gerrit.client.api.ActionContext::put(Lcom/google/gerrit/client/rpc/RestApi;Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),i,b);
+          else
+            @com.google.gerrit.client.api.ActionContext::put(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),i,b);
+        }else{
+          @com.google.gerrit.client.api.ActionContext::put(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),i)
+        }
+      },
       'delete': function(u,b){@com.google.gerrit.client.api.ActionContext::delete(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),b)},
+      JsonString: @com.google.gerrit.client.rpc.NativeString::TYPE,
     };
 
     Plugin.prototype = {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java
index fcd6056..53a3784 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.client.rpc.RestApi;
 import com.google.gerrit.common.PageLinks;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
 import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.rpc.AsyncCallback;
@@ -44,7 +45,8 @@
     invoke(action, api, cb);
   }
 
-  static void invokeProjectAction(ActionInfo action, RestApi api) {
+  static void invokeProjectAction(final Project.NameKey project,
+      ActionInfo action, RestApi api) {
     AsyncCallback<JavaScriptObject> cb = new GerritCallback<JavaScriptObject>() {
       @Override
       public void onSuccess(JavaScriptObject msg) {
@@ -54,7 +56,7 @@
             Window.alert(str.asString());
           }
         }
-        Gerrit.display(PageLinks.ADMIN_PROJECTS);
+        Gerrit.display(PageLinks.toProject(project));
       }
     };
     invoke(action, api, cb);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ProjectGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ProjectGlue.java
index b95f4e0..bce691c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ProjectGlue.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ProjectGlue.java
@@ -35,7 +35,7 @@
       c.button(button);
       ApiGlue.invoke(f, c);
     } else {
-      DefaultActions.invokeProjectAction(action, api);
+      DefaultActions.invokeProjectAction(project, action, api);
     }
   }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.java
index cd1f304..939f946 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.java
@@ -82,7 +82,7 @@
           public void onSuccess(JavaScriptObject msg) {
             Gerrit.display(PageLinks.toChange(changeId));
             hide();
-          };
+          }
         });
   }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
index c8eb13a..75ca6b3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
@@ -56,6 +56,8 @@
 import java.util.TreeSet;
 
 class ReplyBox extends Composite {
+  private static final String CODE_REVIEW = "Code-Review";
+
   interface Binder extends UiBinder<HTMLPanel, ReplyBox> {}
   private static final Binder uiBinder = GWT.create(Binder.class);
 
@@ -67,7 +69,7 @@
   private final PatchSet.Id psId;
   private final String revision;
   private ReviewInput in = ReviewInput.create();
-  private List<Runnable> lgtm;
+  private Runnable lgtm;
 
   @UiField Styles style;
   @UiField NpTextArea message;
@@ -91,7 +93,6 @@
       UIObject.setVisible(labelsParent, false);
     } else {
       Collections.sort(names);
-      lgtm = new ArrayList<Runnable>(names.size());
       renderLabels(names, all, permitted);
     }
   }
@@ -118,11 +119,7 @@
       Scheduler.get().scheduleDeferred(new ScheduledCommand() {
         @Override
         public void execute() {
-          if (message.getValue().startsWith("LGTM")) {
-            for (Runnable r : lgtm) {
-              r.run();
-            }
-          }
+          lgtm.run();
         }
       });
     }
@@ -236,8 +233,8 @@
       }
     }
 
-    if (!group.isEmpty()) {
-      lgtm.add(new Runnable() {
+    if (CODE_REVIEW.equalsIgnoreCase(id) && !group.isEmpty()) {
+      lgtm = new Runnable() {
         @Override
         public void run() {
           for (int i = 0; i < group.size() - 1; i++) {
@@ -245,7 +242,7 @@
           }
           group.get(group.size() - 1).setValue(true, true);
         }
-      });
+      };
     }
   }
 
@@ -269,12 +266,14 @@
     b.setStyleName(style.label_name());
     labelsTable.setWidget(row, 0, b);
 
-    lgtm.add(new Runnable() {
-      @Override
-      public void run() {
-        b.setValue(true, true);
-      }
-    });
+    if (CODE_REVIEW.equalsIgnoreCase(id)) {
+      lgtm = new Runnable() {
+        @Override
+        public void run() {
+          b.setValue(true, true);
+        }
+      };
+    }
   }
 
   private static boolean isCheckBox(Set<Short> values) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/remove_reviewer.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/remove_reviewer.png
index 9e494dd..5a3e6f0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/remove_reviewer.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/remove_reviewer.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/star_filled.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/star_filled.png
index 39bddb1..db1e24e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/star_filled.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/star_filled.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
index 97b0166..03058fe 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
@@ -171,7 +171,7 @@
 
     protected CherryPickInput() {
     }
-  };
+  }
 
   private static class SubmitInput extends JavaScriptObject {
     final native void wait_for_merge(boolean b) /*-{ this.wait_for_merge=b; }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java
index 42705db..48aa02d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java
@@ -453,7 +453,7 @@
           for (Patch p : detail.getPatches()) {
             openWindow(Dispatcher.toPatchUnified(base, p.getKey()));
           }
-        };
+        }
       });
       table.setWidget(row, C_UNIFIED, unified);
     }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java
index fa3d784..d29fb0f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java
@@ -18,7 +18,7 @@
 
 public class ReviewInput extends JavaScriptObject {
   public static enum NotifyHandling {
-    NONE, OWNER, OWNER_REVIEWERS, ALL;
+    NONE, OWNER, OWNER_REVIEWERS, ALL
   }
 
   public static ReviewInput create() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/removeReviewerNormal.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/removeReviewerNormal.png
index 839e8ef..9fde3fa 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/removeReviewerNormal.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/removeReviewerNormal.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/removeReviewerPressed.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/removeReviewerPressed.png
index 2e509ec..e46f0aa 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/removeReviewerPressed.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/removeReviewerPressed.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java
index 826d477..aee30df 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java
@@ -23,8 +23,8 @@
 
 public class DiffApi {
   public enum IgnoreWhitespace {
-    NONE, TRAILING, CHANGED, ALL;
-  };
+    NONE, TRAILING, CHANGED, ALL
+  }
 
   public static void list(int id, String revision,
       AsyncCallback<NativeMap<FileInfo>> cb) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java
index f833509..d5f7819 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java
@@ -93,7 +93,7 @@
   }
 
   public enum IntraLineStatus {
-    OFF, OK, TIMEOUT, FAILURE;
+    OFF, OK, TIMEOUT, FAILURE
   }
 
   public static class FileMeta extends JavaScriptObject {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DisplaySide.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DisplaySide.java
index c51a673..36911bd 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DisplaySide.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DisplaySide.java
@@ -16,5 +16,5 @@
 
 /** Enum representing the side on a side-by-side view */
 enum DisplaySide {
-  A, B;
+  A, B
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SidePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SidePanel.java
index 6a19b30..fba8f19 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SidePanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SidePanel.java
@@ -49,7 +49,7 @@
   }
 
   enum GutterType {
-    COMMENT, DRAFT, INSERT, DELETE, EDIT;
+    COMMENT, DRAFT, INSERT, DELETE, EDIT
   }
 
   @UiField
@@ -114,7 +114,7 @@
     Label gutter = wrapper.gutter;
     final double height = cm.heightAtLine(line, "local");
     final double scrollbarHeight = cmB.getScrollbarV().getClientHeight();
-    double top = height / (double) cmB.getSizer().getClientHeight() *
+    double top = height / cmB.getSizer().getClientHeight() *
         scrollbarHeight +
         cmB.getScrollbarV().getAbsoluteTop();
     if (top == 0) {
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocConstants.java
similarity index 63%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocConstants.java
index a5371d2..f6b7a9d 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocConstants.java
@@ -12,19 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.client.documentation;
 
-import com.google.common.collect.Maps;
+import com.google.gwt.i18n.client.Constants;
 
-import java.util.Map;
+public interface DocConstants extends Constants {
+  String keyReloadSearch();
 
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
-  }
+  String docItemHelp();
+  String docTableColumnTitle();
+  String docTableNone();
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocConstants.properties
new file mode 100644
index 0000000..b48c507
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocConstants.properties
@@ -0,0 +1,5 @@
+keyReloadSearch = Reload documentation list
+
+docItemHelp = documentation
+docTableColumnTitle = Title
+docTableNone = (None)
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocInfo.java
new file mode 100644
index 0000000..6235186
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocInfo.java
@@ -0,0 +1,31 @@
+// 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.client.documentation;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.JavaScriptObject;
+
+public class DocInfo extends JavaScriptObject {
+
+  public final native String title() /*-{ return this.title; }-*/;
+  public final native String url() /*-{ return this.url; }-*/;
+
+  protected DocInfo() {
+  }
+
+  public final String getFullUrl() {
+    return GWT.getHostPageBaseURL() + url();
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocMessages.java
similarity index 63%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocMessages.java
index a5371d2..df62b92 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocMessages.java
@@ -12,19 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.client.documentation;
 
-import com.google.common.collect.Maps;
+import com.google.gwt.i18n.client.Messages;
 
-import java.util.Map;
-
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
-  }
+public interface DocMessages extends Messages {
+  String docQueryWindowTitle(String query);
+  String docQueryPageTitle(String query);
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocMessages.properties
new file mode 100644
index 0000000..8810a4a
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocMessages.properties
@@ -0,0 +1,2 @@
+docQueryWindowTitle = {0}
+docQueryPageTitle = Search for {0} in documentation
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocScreen.java
new file mode 100644
index 0000000..0a87d29
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocScreen.java
@@ -0,0 +1,78 @@
+// 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.client.documentation;
+
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.rpc.RestApi;
+import com.google.gerrit.client.ui.Screen;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtorm.client.KeyUtil;
+
+public class DocScreen extends Screen {
+  private static final String URI = "/Documentation/";
+
+  private DocTable table;
+  private final String query;
+
+  public DocScreen(String query) {
+    this.query = KeyUtil.decode(query);
+  }
+
+  @Override
+  protected void onInitUI() {
+    super.onInitUI();
+
+    table = new DocTable();
+    table.setSavePointerId(query);
+    add(table);
+
+    setWindowTitle(Util.M.docQueryWindowTitle(query));
+    setPageTitle(Util.M.docQueryPageTitle(query));
+  }
+
+  @Override
+  protected void onLoad() {
+    super.onLoad();
+    doQuery();
+  }
+
+  @Override
+  public void registerKeys() {
+    super.registerKeys();
+    table.setRegisterKeys(true);
+  }
+
+  private AsyncCallback<JsArray<DocInfo>> loadCallback() {
+    return new GerritCallback<JsArray<DocInfo>>() {
+      @Override
+      public void onSuccess(JsArray<DocInfo> result) {
+        displayResults(result);
+        display();
+      }
+    };
+  }
+
+  private void displayResults(JsArray<DocInfo> result) {
+    table.display(result);
+    table.finishDisplay();
+  }
+
+  private void doQuery() {
+    RestApi call = new RestApi(URI);
+    call.addParameterRaw("q", KeyUtil.encode(query));
+    call.get(loadCallback());
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocTable.java
new file mode 100644
index 0000000..f176372
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocTable.java
@@ -0,0 +1,125 @@
+// 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.client.documentation;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.ui.NavigationTable;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwt.user.client.ui.HTMLTable.Cell;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+
+class DocTable extends NavigationTable<DocInfo> {
+  private static final int C_TITLE = 1;
+
+  private int rows = 0;
+  private int dataBeginRow = 0;
+
+  public DocTable() {
+    super(Util.C.docItemHelp());
+
+    table.setText(0, C_TITLE, Util.C.docTableColumnTitle());
+
+    FlexCellFormatter fmt = table.getFlexCellFormatter();
+    fmt.addStyleName(0, C_TITLE, Gerrit.RESOURCES.css().dataHeader());
+
+    table.addClickHandler(new ClickHandler() {
+      @Override
+      public void onClick(ClickEvent event) {
+        Cell cell = table.getCellForEvent(event);
+        if (cell == null) {
+          return;
+        }
+        if (getRowItem(cell.getRowIndex()) != null) {
+          movePointerTo(cell.getRowIndex());
+        }
+      }
+    });
+  }
+
+  @Override
+  protected Object getRowItemKey(DocInfo item) {
+    return item.url();
+  }
+
+  @Override
+  protected void onOpenRow(int row) {
+    DocInfo d = getRowItem(row);
+    Window.Location.assign(d.getFullUrl());
+  }
+
+  private void insertNoneRow(int row) {
+    table.insertRow(row);
+    table.setText(row, 0, Util.C.docTableNone());
+    FlexCellFormatter fmt = table.getFlexCellFormatter();
+    fmt.setStyleName(row, 0, Gerrit.RESOURCES.css().emptySection());
+  }
+
+  private void insertDocRow(int row) {
+    table.insertRow(row);
+    applyDataRowStyle(row);
+  }
+
+  @Override
+  protected void applyDataRowStyle(int row) {
+    super.applyDataRowStyle(row);
+    CellFormatter fmt = table.getCellFormatter();
+    fmt.addStyleName(row, C_TITLE, Gerrit.RESOURCES.css().dataCell());
+    fmt.addStyleName(row, C_TITLE, Gerrit.RESOURCES.css().cSUBJECT());
+  }
+
+  private void populateDocRow(int row, DocInfo d) {
+    table.setWidget(row, C_TITLE, new DocLink(d));
+    setRowItem(row, d);
+  }
+
+  public void display(JsArray<DocInfo> docList) {
+    int sz = docList != null ? docList.length() : 0;
+    boolean hadData = rows > 0;
+
+    if (hadData) {
+      while (sz < rows) {
+        table.removeRow(dataBeginRow);
+        rows--;
+      }
+    } else {
+      table.removeRow(dataBeginRow);
+    }
+
+    if (sz == 0) {
+      insertNoneRow(dataBeginRow);
+      return;
+    }
+
+    while (rows < sz) {
+      insertDocRow(dataBeginRow + rows);
+      rows++;
+    }
+    for (int i = 0; i < sz; i++) {
+      populateDocRow(dataBeginRow + i, docList.get(i));
+    }
+  }
+
+  public static class DocLink extends Anchor {
+    public DocLink(DocInfo d) {
+      super(com.google.gerrit.client.changes.Util.cropSubject(d.title()));
+      setHref(d.getFullUrl());
+    }
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/Util.java
similarity index 63%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/Util.java
index a5371d2..273ead8 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/Util.java
@@ -12,19 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.client.documentation;
 
-import com.google.common.collect.Maps;
+import com.google.gwt.core.client.GWT;
 
-import java.util.Map;
-
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
-  }
+public class Util {
+  public static final DocConstants C = GWT.create(DocConstants.class);
+  public static final DocMessages M = GWT.create(DocMessages.class);
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/downloadIcon.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/downloadIcon.png
index 22ff495..1a6520e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/downloadIcon.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/downloadIcon.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/draftComments.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/draftComments.png
index 31c770f..276912a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/draftComments.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/draftComments.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editText.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editText.png
index 188e1c1..2927275 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editText.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editText.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/greenCheck.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/greenCheck.png
index cd70687..207c0e7 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/greenCheck.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/greenCheck.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchLine.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchLine.java
index 9726bbb..4863af2 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchLine.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchLine.java
@@ -16,7 +16,7 @@
 
 class PatchLine {
   static enum Type {
-    DELETE, INSERT, REPLACE, CONTEXT;
+    DELETE, INSERT, REPLACE, CONTEXT
   }
 
   private PatchLine.Type type;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.java
index 9795a64..08799a5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.java
@@ -147,7 +147,7 @@
         }
       }
       toggleEnabledStatus(on);
-    };
+    }
   }
 
   public void setEnableSmallFileFeatures(final boolean on) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
index 62dbf1c7..ebae86a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
@@ -110,6 +110,15 @@
         });
   }
 
+  public static void getChildren(Project.NameKey name, boolean recursive,
+      AsyncCallback<JsArray<ProjectInfo>> cb) {
+    RestApi view = project(name).view("children");
+    if (recursive) {
+      view.addParameterTrue("recursive");
+    }
+    view.get(cb);
+  }
+
   public static void getDescription(Project.NameKey name,
       AsyncCallback<NativeString> cb) {
     project(name).view("description").get(cb);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java
index 80c1feb..cab45b5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java
@@ -28,6 +28,12 @@
   public final native String name() /*-{ return this.name; }-*/;
   public final native String description() /*-{ return this.description; }-*/;
 
+  public final Project.State state() {
+    return Project.State.valueOf(getStringState());
+  }
+
+  private final native String getStringState() /*-{ return this.state; }-*/;
+
   @Override
   public final String getDisplayString() {
     if (description() != null) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/queryIcon.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/queryIcon.png
index 5aace51..5ebf2cb 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/queryIcon.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/queryIcon.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/readOnly.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/readOnly.png
new file mode 100644
index 0000000..62e89f9
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/readOnly.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/redNot.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/redNot.png
index 4e83a8f..99834fd 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/redNot.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/redNot.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeString.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeString.java
index be4cfd6..a3dce43 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeString.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeString.java
@@ -19,10 +19,15 @@
 
 /** Wraps a String that was returned from a JSON API. */
 public final class NativeString extends JavaScriptObject {
-  private static final JavaScriptObject TYPE = init();
+  public static final JavaScriptObject TYPE = init();
 
-  private static final native JavaScriptObject init()
-  /*-{ return function(s){this.s=s} }-*/;
+  private static final native JavaScriptObject init() /*-{
+    var T = function(s){this.s=s};
+    T.prototype = {
+      get: function(){return this.s},
+    };
+    return T;
+  }-*/;
 
   static final NativeString wrap(String s) {
     return wrap0(TYPE, s);
@@ -32,7 +37,6 @@
   /*-{ return new T(s) }-*/;
 
   public final native String asString() /*-{ return this.s; }-*/;
-  private final native void set(String v) /*-{ this.s = v; }-*/;
 
   public static final AsyncCallback<NativeString>
   unwrap(final AsyncCallback<String> cb) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
index 4666d34..5ae3091 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
@@ -124,29 +124,31 @@
         }
 
       } else if (200 <= status && status < 300) {
-        if (!isJsonBody(res)) {
+        T data;
+        if (isTextBody(res)) {
+          data = NativeString.wrap(res.getText()).cast();
+        } else if (isJsonBody(res)) {
+          try {
+            // javac generics bug
+            data = RestApi.<T>cast(parseJson(res));
+          } catch (JSONException e) {
+            if (!background) {
+              RpcStatus.INSTANCE.onRpcComplete();
+            }
+            cb.onFailure(new StatusCodeException(SC_BAD_RESPONSE,
+                "Invalid JSON: " + e.getMessage()));
+            return;
+          }
+        } else {
           if (!background) {
             RpcStatus.INSTANCE.onRpcComplete();
           }
           cb.onFailure(new StatusCodeException(SC_BAD_RESPONSE, "Expected "
-              + JSON_TYPE + "; received Content-Type: "
+              + JSON_TYPE + " or " + TEXT_TYPE + "; received Content-Type: "
               + res.getHeader("Content-Type")));
           return;
         }
 
-        T data;
-        try {
-          // javac generics bug
-          data = RestApi.<T>cast(parseJson(res));
-        } catch (JSONException e) {
-          if (!background) {
-            RpcStatus.INSTANCE.onRpcComplete();
-          }
-          cb.onFailure(new StatusCodeException(SC_BAD_RESPONSE,
-              "Invalid JSON: " + e.getMessage()));
-          return;
-        }
-
         cb.onSuccess(data);
         if (!background) {
           RpcStatus.INSTANCE.onRpcComplete();
@@ -336,6 +338,11 @@
     send(PUT, cb);
   }
 
+  public <T extends JavaScriptObject> void put(String content,
+      AsyncCallback<T> cb) {
+    sendRaw(PUT, content, cb);
+  }
+
   public <T extends JavaScriptObject> void put(
       JavaScriptObject content,
       AsyncCallback<T> cb) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ParentProjectBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ParentProjectBox.java
new file mode 100644
index 0000000..a03cb34
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ParentProjectBox.java
@@ -0,0 +1,101 @@
+// 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.client.ui;
+
+import com.google.gerrit.client.projects.ProjectApi;
+import com.google.gerrit.client.projects.ProjectInfo;
+import com.google.gerrit.client.rpc.Natives;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.SuggestBox;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class ParentProjectBox extends Composite {
+  private final NpTextBox textBox;
+  private final SuggestBox suggestBox;
+  private final ParentProjectNameSuggestOracle suggestOracle;
+
+  public ParentProjectBox() {
+    textBox = new NpTextBox();
+    suggestOracle = new ParentProjectNameSuggestOracle();
+    suggestBox = new SuggestBox(suggestOracle, textBox);
+    initWidget(suggestBox);
+  }
+
+  public void setVisibleLength(int len) {
+    textBox.setVisibleLength(len);
+  }
+
+  public void setProject(final Project.NameKey project) {
+    suggestOracle.setProject(project);
+  }
+
+  public void setParentProject(final Project.NameKey parent) {
+    suggestBox.setText(parent != null ? parent.get() : "");
+  }
+
+  public Project.NameKey getParentProjectName() {
+    final String projectName = suggestBox.getText().trim();
+    if (projectName.isEmpty()) {
+      return null;
+    }
+    return new Project.NameKey(projectName);
+  }
+
+  private static class ParentProjectNameSuggestOracle extends ProjectNameSuggestOracle {
+    private Set<String> exclude = new HashSet<String>();
+
+    public void setProject(Project.NameKey project) {
+      exclude.clear();
+      exclude.add(project.get());
+      ProjectApi.getChildren(project, true, new AsyncCallback<JsArray<ProjectInfo>>() {
+        @Override
+        public void onSuccess(JsArray<ProjectInfo> result) {
+          for (ProjectInfo p : Natives.asList(result)) {
+            exclude.add(p.name());
+          }
+        }
+
+        @Override
+        public void onFailure(Throwable caught) {
+        }
+      });
+    }
+
+    @Override
+    public void _onRequestSuggestions(Request req, final Callback callback) {
+      super._onRequestSuggestions(req, new Callback() {
+        public void onSuggestionsReady(Request request, Response response) {
+          if (exclude.size() > 0) {
+            Set<Suggestion> filteredSuggestions =
+                new HashSet<Suggestion>(response.getSuggestions());
+            for (Suggestion s : response.getSuggestions()) {
+              if (exclude.contains(s.getReplacementString())) {
+                filteredSuggestions.remove(s);
+              }
+            }
+            response.setSuggestions(filteredSuggestions);
+          }
+          callback.onSuggestionsReady(request, response);
+        }
+      });
+    }
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java
index 052878b..289e6fe 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java
@@ -25,6 +25,10 @@
 import java.util.List;
 
 public class ProjectsTable extends NavigationTable<ProjectInfo> {
+  public static final int C_STATE = 1;
+  public static final int C_NAME = 2;
+  public static final int C_DESCRIPTION = 3;
+  public static final int C_REPO_BROWSER = 4;
 
   public ProjectsTable() {
     super(Util.C.projectItemHelp());
@@ -32,12 +36,16 @@
   }
 
   protected void initColumnHeaders() {
-    table.setText(0, 1, Util.C.projectName());
-    table.setText(0, 2, Util.C.projectDescription());
+    table.setText(0, C_STATE, Util.C.projectStateAbbrev());
+    table.getCellFormatter().getElement(0, C_STATE)
+        .setTitle(Util.C.projectStateHelp());
+    table.setText(0, C_NAME, Util.C.projectName());
+    table.setText(0, C_DESCRIPTION, Util.C.projectDescription());
 
     final FlexCellFormatter fmt = table.getFlexCellFormatter();
-    fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().dataHeader());
-    fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
+    fmt.addStyleName(0, C_STATE, Gerrit.RESOURCES.css().iconHeader());
+    fmt.addStyleName(0, C_NAME, Gerrit.RESOURCES.css().dataHeader());
+    fmt.addStyleName(0, C_DESCRIPTION, Gerrit.RESOURCES.css().dataHeader());
   }
 
   @Override
@@ -75,16 +83,18 @@
     applyDataRowStyle(row);
 
     final FlexCellFormatter fmt = table.getFlexCellFormatter();
-    fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().dataCell());
-    fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().projectNameColumn());
-    fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().dataCell());
+    fmt.addStyleName(row, C_STATE, Gerrit.RESOURCES.css().iconCell());
+    fmt.addStyleName(row, C_NAME, Gerrit.RESOURCES.css().dataCell());
+    fmt.addStyleName(row, C_NAME, Gerrit.RESOURCES.css().projectNameColumn());
+    fmt.addStyleName(row, C_DESCRIPTION, Gerrit.RESOURCES.css().dataCell());
 
     populate(row, k);
   }
 
   protected void populate(final int row, final ProjectInfo k) {
-    table.setText(row, 1, k.name());
-    table.setText(row, 2, k.description());
+    table.setText(row, C_STATE, k.state().toString());
+    table.setText(row, C_NAME, k.name());
+    table.setText(row, C_DESCRIPTION, k.description());
 
     setRowItem(row, k);
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java
index 0cabdda..01a90e3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java
@@ -71,7 +71,7 @@
   }
 
   private static enum Cols {
-    West, Title, East, FarEast;
+    West, Title, East, FarEast
   }
 
   protected void onInitUI() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.java
index 1919cd3..bcfb394 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.java
@@ -23,4 +23,6 @@
   String projectName();
   String projectDescription();
   String projectItemHelp();
+  String projectStateAbbrev();
+  String projectStateHelp();
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.properties
index 8a72355..1e0e185 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.properties
@@ -3,4 +3,6 @@
 
 projectName = Project Name
 projectDescription = Project Description
-projectItemHelp = project
\ No newline at end of file
+projectItemHelp = project
+projectStateAbbrev = S
+projectStateHelp = State
\ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
index c5a047c..2ebe326 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
@@ -68,7 +68,7 @@
   }-*/;
 
   public enum LineClassWhere {
-    TEXT, BACKGROUND, WRAP;
+    TEXT, BACKGROUND, WRAP
   }
 
   public final void addLineClass(int line, LineClassWhere where,
@@ -201,7 +201,7 @@
 
   public final FromTo getSelectedRange() {
     return FromTo.create(getCursor("start"), getCursor("end"));
-  };
+  }
 
   public final native void setCursor(LineCharacter lineCh) /*-{
     this.setCursor(lineCh);
diff --git a/gerrit-httpd/BUCK b/gerrit-httpd/BUCK
index 512e6e6..c20b589 100644
--- a/gerrit-httpd/BUCK
+++ b/gerrit-httpd/BUCK
@@ -1,4 +1,6 @@
-SRCS = glob(['src/main/java/**/*.java'])
+SRCS = glob(
+  ['src/main/java/**/*.java'],
+)
 RESOURCES = glob(['src/main/resources/**/*'])
 
 java_library2(
@@ -30,6 +32,7 @@
     '//lib/jgit:jgit',
     '//lib/jgit:jgit-servlet',
     '//lib/log:api',
+    '//lib/lucene:core',
   ],
   compile_deps = ['//lib:servlet-api-3_0'],
   visibility = ['PUBLIC'],
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CookieBase64.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CookieBase64.java
index 3bcdcd2..fab9f1b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CookieBase64.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CookieBase64.java
@@ -33,8 +33,9 @@
   }
 
   private static int fill(final char[] out, int o, final char f, final int l) {
-    for (char c = f; c <= l; c++)
+    for (char c = f; c <= l; c++) {
       out[o++] = c;
+    }
     return o;
   }
 
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
index 2980159..61ea464 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
@@ -127,7 +127,7 @@
         cfg, "change", null, "updateDelay", 30, TimeUnit.SECONDS));
     config.setChangeScreen(cfg.getEnum(
         "gerrit", null, "changeScreen",
-        AccountGeneralPreferences.ChangeScreen.OLD_UI));
+        AccountGeneralPreferences.ChangeScreen.CHANGE_SCREEN2));
 
     config.setReportBugUrl(cfg.getString("gerrit", null, "reportBugUrl"));
     if (config.getReportBugUrl() == null) {
@@ -136,7 +136,7 @@
       config.setReportBugUrl(null);
     }
 
-    config.setGitBasicAuth(authConfig.isGitBasichAuth());
+    config.setGitBasicAuth(authConfig.isGitBasicAuth());
 
     final Set<Account.FieldName> fields = new HashSet<Account.FieldName>();
     for (final Account.FieldName n : Account.FieldName.values()) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpModule.java
index 6ca9949..4f4c783 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpModule.java
@@ -34,7 +34,7 @@
     Class<? extends Filter> authFilter;
     if (authConfig.isTrustContainerAuth()) {
       authFilter = ContainerAuthFilter.class;
-    } else if (authConfig.isGitBasichAuth()) {
+    } else if (authConfig.isGitBasicAuth()) {
       authFilter = ProjectBasicAuthFilter.class;
     } else {
       authFilter = ProjectDigestFilter.class;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
index bf39bfb..3c4dfc5 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
@@ -30,6 +30,7 @@
 import com.google.gerrit.httpd.rpc.change.ChangesRestApiServlet;
 import com.google.gerrit.httpd.rpc.change.DeprecatedChangeQueryServlet;
 import com.google.gerrit.httpd.rpc.config.ConfigRestApiServlet;
+import com.google.gerrit.httpd.rpc.doc.QueryDocumentationFilter;
 import com.google.gerrit.httpd.rpc.group.GroupsRestApiServlet;
 import com.google.gerrit.httpd.rpc.project.ProjectsRestApiServlet;
 import com.google.gerrit.reviewdb.client.Change;
@@ -110,6 +111,8 @@
     serveRegex("^/(?:a/)?groups/(.*)?$").with(GroupsRestApiServlet.class);
     serveRegex("^/(?:a/)?projects/(.*)?$").with(ProjectsRestApiServlet.class);
 
+    filter("/Documentation/").through(QueryDocumentationFilter.class);
+
     if (cfg.deprecatedQuery) {
       serve("/query").with(DeprecatedChangeQueryServlet.class);
     }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
index 5593baf..3443968 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
@@ -31,7 +31,6 @@
 import com.google.gerrit.server.RemotePeer;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.config.GerritRequestModule;
 import com.google.gerrit.server.contact.ContactStore;
 import com.google.gerrit.server.contact.ContactStoreProvider;
@@ -46,7 +45,7 @@
 
 import java.net.SocketAddress;
 
-public class WebModule extends FactoryModule {
+public class WebModule extends LifecycleModule {
   private final AuthConfig authConfig;
   private final UrlModule.UrlConfig urlConfig;
   private final boolean wantSSL;
@@ -132,11 +131,6 @@
     bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
         HttpRemotePeerProvider.class).in(RequestScoped.class);
 
-    install(new LifecycleModule() {
-      @Override
-      protected void configure() {
-        listener().toInstance(registerInParentInjectors());
-      }
-    });
+    listener().toInstance(registerInParentInjectors());
   }
 }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
index 28e361c..2dde16e 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
@@ -168,7 +168,7 @@
     Element userlistElement = HtmlDomUtil.find(doc, "userlist");
     ReviewDb db = schema.open();
     try {
-      ResultSet<Account> accounts = db.accounts().firstNById(5);
+      ResultSet<Account> accounts = db.accounts().firstNById(100);
       for (Account a : accounts) {
         String displayName;
         if (a.getUserName() != null) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 517b017..2e1cc06 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -607,7 +607,7 @@
     throw new InstantiationException("Cannot make " + type);
   }
 
-  private static void replyJson(@Nullable HttpServletRequest req,
+  public static void replyJson(@Nullable HttpServletRequest req,
       HttpServletResponse res,
       Multimap<String, String> config,
       Object result)
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/doc/QueryDocumentationFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/doc/QueryDocumentationFilter.java
new file mode 100644
index 0000000..002b520
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/doc/QueryDocumentationFilter.java
@@ -0,0 +1,74 @@
+// 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.httpd.rpc.doc;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.gerrit.httpd.restapi.RestApiServlet;
+import com.google.gerrit.server.documentation.QueryDocumentationExecutor;
+import com.google.gerrit.server.documentation.QueryDocumentationExecutor.DocQueryException;
+import com.google.gerrit.server.documentation.QueryDocumentationExecutor.DocResult;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Singleton
+public class QueryDocumentationFilter implements Filter {
+  private final QueryDocumentationExecutor searcher;
+
+  @Inject
+  QueryDocumentationFilter(QueryDocumentationExecutor searcher) {
+    this.searcher = searcher;
+  }
+
+  @Override
+  public void init(FilterConfig filterConfig) {
+  }
+
+  @Override
+  public void destroy() {
+  }
+
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+      throws IOException, ServletException {
+    HttpServletRequest req = (HttpServletRequest) request;
+    if ("GET".equals(req.getMethod())
+        && !Strings.isNullOrEmpty(req.getParameter("q"))) {
+      HttpServletResponse rsp = (HttpServletResponse) response;
+      try {
+        List<DocResult> result = searcher.doQuery(request.getParameter("q"));
+        Multimap<String, String> config = LinkedHashMultimap.create();
+        RestApiServlet.replyJson(req, rsp, config, result);
+      } catch (DocQueryException e) {
+        rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+      }
+    } else {
+      chain.doFilter(request, response);
+    }
+  }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
index 0dd3d16..57be31e 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
@@ -19,12 +19,15 @@
 import com.google.gerrit.common.data.ProjectAccess;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.config.AllProjectsNameProvider;
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.SetParent;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
 
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -35,9 +38,11 @@
 
 class ChangeProjectAccess extends ProjectAccessHandler<ProjectAccess> {
   interface Factory {
-    ChangeProjectAccess create(@Assisted Project.NameKey projectName,
+    ChangeProjectAccess create(
+        @Assisted("projectName") Project.NameKey projectName,
         @Nullable @Assisted ObjectId base,
         @Assisted List<AccessSection> sectionList,
+        @Nullable @Assisted("parentProjectName") Project.NameKey parentProjectName,
         @Nullable @Assisted String message);
   }
 
@@ -45,17 +50,21 @@
   private final ProjectCache projectCache;
 
   @Inject
-  ChangeProjectAccess(final ProjectAccessFactory.Factory projectAccessFactory,
-      final ProjectControl.Factory projectControlFactory,
-      final ProjectCache projectCache, final GroupBackend groupBackend,
-      final MetaDataUpdate.User metaDataUpdateFactory,
+  ChangeProjectAccess(ProjectAccessFactory.Factory projectAccessFactory,
+      ProjectControl.Factory projectControlFactory,
+      ProjectCache projectCache, GroupBackend groupBackend,
+      MetaDataUpdate.User metaDataUpdateFactory,
+      AllProjectsNameProvider allProjects,
+      Provider<SetParent> setParent,
 
-      @Assisted final Project.NameKey projectName,
-      @Nullable @Assisted final ObjectId base,
+      @Assisted("projectName") Project.NameKey projectName,
+      @Nullable @Assisted ObjectId base,
       @Assisted List<AccessSection> sectionList,
+      @Nullable @Assisted("parentProjectName") Project.NameKey parentProjectName,
       @Nullable @Assisted String message) {
     super(projectControlFactory, groupBackend, metaDataUpdateFactory,
-        projectName, base, sectionList, message, true);
+        allProjects, setParent, projectName, base, sectionList,
+        parentProjectName, message, true);
     this.projectAccessFactory = projectAccessFactory;
     this.projectCache = projectCache;
   }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
index 2c5681b..6e7150d 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
@@ -203,6 +203,8 @@
     detail.setOwnerOf(ownerOf);
     detail.setCanUpload(pc.isOwner()
         || (metaConfigControl.isVisible() && metaConfigControl.canUpload()));
+    detail.setCanChangeParent(pc.getCurrentUser().getCapabilities()
+        .canAdministrateServer());
     detail.setConfigVisible(pc.isOwner() || metaConfigControl.isVisible());
     detail.setLabelTypes(pc.getLabelTypes());
     return detail;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
index 0a50a38..971c4b3 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
@@ -22,16 +22,23 @@
 import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.common.errors.InvalidNameException;
 import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.common.errors.UpdateParentFailedException;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.httpd.rpc.Handler;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupBackends;
+import com.google.gerrit.server.config.AllProjectsNameProvider;
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.project.ProjectControl;
 import com.google.gerrit.server.project.RefControl;
+import com.google.gerrit.server.project.SetParent;
 import com.google.gwtorm.server.OrmException;
+import com.google.inject.Provider;
 
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -47,27 +54,32 @@
   private final ProjectControl.Factory projectControlFactory;
   protected final GroupBackend groupBackend;
   private final MetaDataUpdate.User metaDataUpdateFactory;
+  private final AllProjectsNameProvider allProjects;
+  private final Provider<SetParent> setParent;
 
   protected final Project.NameKey projectName;
   protected final ObjectId base;
   private List<AccessSection> sectionList;
+  private final Project.NameKey parentProjectName;
   protected String message;
   private boolean checkIfOwner;
 
-  protected ProjectAccessHandler(
-      final ProjectControl.Factory projectControlFactory,
-      final GroupBackend groupBackend,
-      final MetaDataUpdate.User metaDataUpdateFactory,
-      final Project.NameKey projectName, final ObjectId base,
-      final List<AccessSection> sectionList, final String message,
-      final boolean checkIfOwner) {
+  protected ProjectAccessHandler(ProjectControl.Factory projectControlFactory,
+      GroupBackend groupBackend, MetaDataUpdate.User metaDataUpdateFactory,
+      AllProjectsNameProvider allProjects, Provider<SetParent> setParent,
+      Project.NameKey projectName, ObjectId base,
+      List<AccessSection> sectionList, Project.NameKey parentProjectName,
+      String message, boolean checkIfOwner) {
     this.projectControlFactory = projectControlFactory;
     this.groupBackend = groupBackend;
     this.metaDataUpdateFactory = metaDataUpdateFactory;
+    this.allProjects = allProjects;
+    this.setParent = setParent;
 
     this.projectName = projectName;
     this.base = base;
     this.sectionList = sectionList;
+    this.parentProjectName = parentProjectName;
     this.message = message;
     this.checkIfOwner = checkIfOwner;
   }
@@ -75,7 +87,7 @@
   @Override
   public final T call() throws NoSuchProjectException, IOException,
       ConfigInvalidException, InvalidNameException, NoSuchGroupException,
-      OrmException {
+      OrmException, UpdateParentFailedException {
     final ProjectControl projectControl =
         projectControlFactory.controlFor(projectName);
 
@@ -120,6 +132,20 @@
         }
       }
 
+      if (!config.getProject().getNameKey().equals(allProjects.get()) &&
+          !config.getProject().getParent(allProjects.get()).equals(parentProjectName)) {
+        try {
+          setParent.get().validateParentUpdate(projectControl, parentProjectName.get());
+        } catch (AuthException e) {
+          throw new UpdateParentFailedException(e.getMessage(), e);
+        } catch (ResourceConflictException e) {
+          throw new UpdateParentFailedException(e.getMessage(), e);
+        } catch (UnprocessableEntityException e) {
+          throw new UpdateParentFailedException(e.getMessage(), e);
+        }
+        config.getProject().setParentName(parentProjectName);
+      }
+
       if (message != null && !message.isEmpty()) {
         if (!message.endsWith("\n")) {
           message += "\n";
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
index 66ba75c..c378701 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
@@ -56,14 +56,16 @@
   @Override
   public void changeProjectAccess(Project.NameKey projectName,
       String baseRevision, String msg, List<AccessSection> sections,
-      AsyncCallback<ProjectAccess> cb) {
-    changeProjectAccessFactory.create(projectName, getBase(baseRevision), sections, msg).to(cb);
+      Project.NameKey parentProjectName, AsyncCallback<ProjectAccess> cb) {
+    changeProjectAccessFactory.create(projectName, getBase(baseRevision),
+        sections, parentProjectName, msg).to(cb);
   }
 
   @Override
   public void reviewProjectAccess(Project.NameKey projectName,
       String baseRevision, String msg, List<AccessSection> sections,
-      AsyncCallback<Change.Id> cb) {
-    reviewProjectAccessFactory.create(projectName, getBase(baseRevision), sections, msg).to(cb);
+      Project.NameKey parentProjectName, AsyncCallback<Change.Id> cb) {
+    reviewProjectAccessFactory.create(projectName, getBase(baseRevision),
+        sections, parentProjectName, msg).to(cb);
   }
 }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
index 27a02d9..3f4030d 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
@@ -31,6 +31,7 @@
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.change.PostReviewers;
+import com.google.gerrit.server.config.AllProjectsNameProvider;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
@@ -39,6 +40,7 @@
 import com.google.gerrit.server.patch.PatchSetInfoFactory;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.SetParent;
 import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -60,9 +62,11 @@
       LoggerFactory.getLogger(ReviewProjectAccess.class);
 
   interface Factory {
-    ReviewProjectAccess create(@Assisted Project.NameKey projectName,
+    ReviewProjectAccess create(
+        @Assisted("projectName") Project.NameKey projectName,
         @Nullable @Assisted ObjectId base,
         @Assisted List<AccessSection> sectionList,
+        @Nullable @Assisted("parentProjectName") Project.NameKey parentProjectName,
         @Nullable @Assisted String message);
   }
 
@@ -84,13 +88,17 @@
       ChangeControl.GenericFactory changeFactory,
       ChangeIndexer indexer, ChangeHooks hooks,
       CreateChangeSender.Factory createChangeSenderFactory,
+      AllProjectsNameProvider allProjects,
+      Provider<SetParent> setParent,
 
-      @Assisted Project.NameKey projectName,
+      @Assisted("projectName") Project.NameKey projectName,
       @Nullable @Assisted ObjectId base,
       @Assisted List<AccessSection> sectionList,
+      @Nullable @Assisted("parentProjectName") Project.NameKey parentProjectName,
       @Nullable @Assisted String message) {
     super(projectControlFactory, groupBackend, metaDataUpdateFactory,
-        projectName, base, sectionList, message, false);
+        allProjects, setParent, projectName, base, sectionList,
+        parentProjectName, message, false);
     this.db = db;
     this.user = user;
     this.patchSetInfoFactory = patchSetInfoFactory;
diff --git a/gerrit-httpd/src/test/java/com/google/gerrit/httpd/GitWebConfigTest.java b/gerrit-httpd/src/test/java/com/google/gerrit/httpd/GitWebConfigTest.java
index c834b94..26cdd8a 100644
--- a/gerrit-httpd/src/test/java/com/google/gerrit/httpd/GitWebConfigTest.java
+++ b/gerrit-httpd/src/test/java/com/google/gerrit/httpd/GitWebConfigTest.java
@@ -14,19 +14,24 @@
 
 package com.google.gerrit.httpd;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
-public class GitWebConfigTest extends TestCase {
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class GitWebConfigTest {
 
   private static final String VALID_CHARACTERS = "*()";
   private static final String SOME_INVALID_CHARACTERS = "09AZaz$-_.+!',";
 
+  @Test
   public void testValidPathSeparator() {
     for(char c : VALID_CHARACTERS.toCharArray()) {
       assertTrue("valid character rejected: " + c, GitWebConfig.isValidPathSeparator(c));
     }
   }
 
+  @Test
   public void testInalidPathSeparator() {
     for(char c : SOME_INVALID_CHARACTERS.toCharArray()) {
       assertFalse("invalid character accepted: " + c, GitWebConfig.isValidPathSeparator(c));
diff --git a/gerrit-httpd/src/test/java/com/google/gerrit/httpd/restapi/ParameterParserTest.java b/gerrit-httpd/src/test/java/com/google/gerrit/httpd/restapi/ParameterParserTest.java
index ffbc7f3..8533a9c 100644
--- a/gerrit-httpd/src/test/java/com/google/gerrit/httpd/restapi/ParameterParserTest.java
+++ b/gerrit-httpd/src/test/java/com/google/gerrit/httpd/restapi/ParameterParserTest.java
@@ -21,9 +21,11 @@
 import com.google.gson.JsonObject;
 import com.google.gson.JsonPrimitive;
 
-import junit.framework.TestCase;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
 
-public class ParameterParserTest extends TestCase {
+public class ParameterParserTest {
+  @Test
   public void testConvertFormToJson() throws BadRequestException {
     JsonObject obj = ParameterParser.formToJson(
         ImmutableMap.of(
diff --git a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
index 2dd20a2..3ca3619 100644
--- a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
+++ b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
@@ -190,7 +190,7 @@
     try {
       path = getDistributionArchive();
     } catch (FileNotFoundException e) {
-      if (NOT_ARCHIVED == e.getMessage()) {
+      if (NOT_ARCHIVED.equals(e.getMessage())) {
         // Assume the CLASSPATH was made complete by the calling process,
         // as we are likely being run from within a developer's IDE.
         //
@@ -302,6 +302,7 @@
   }
 
   private volatile static File myArchive;
+  private volatile static File myHome;
 
   /**
    * Locate the JAR/WAR file we were launched from.
@@ -459,42 +460,26 @@
     return tmp;
   }
 
+  /**
+   * Provide path to a working directory
+   *
+   * @return local path of the working directory or null if cannot be determined
+   */
+  public static File getHomeDirectory() {
+    if (myHome == null) {
+      myHome = locateHomeDirectory();
+    }
+    return myHome;
+  }
+
+
   private static File tmproot() {
     File tmp;
     String gerritTemp = System.getenv("GERRIT_TMP");
     if (gerritTemp != null && gerritTemp.length() > 0) {
       tmp = new File(gerritTemp);
     } else {
-      // Try to find the user's home directory. If we can't find it
-      // return null so the JVM's default temporary directory is used
-      // instead. This is probably /tmp or /var/tmp.
-      //
-      String userHome = System.getProperty("user.home");
-      if (userHome == null || "".equals(userHome)) {
-        userHome = System.getenv("HOME");
-        if (userHome == null || "".equals(userHome)) {
-          System.err.println("warning: cannot determine home directory");
-          System.err.println("warning: using system temporary directory instead");
-          return null;
-        }
-      }
-
-      // Ensure the home directory exists. If it doesn't, try to make it.
-      //
-      final File home = new File(userHome);
-      if (!home.exists()) {
-        if (home.mkdirs()) {
-          System.err.println("warning: created " + home.getAbsolutePath());
-        } else {
-          System.err.println("warning: " + home.getAbsolutePath() + " not found");
-          System.err.println("warning: using system temporary directory instead");
-          return null;
-        }
-      }
-
-      // Use $HOME/.gerritcodereview/tmp for our temporary file area.
-      //
-      tmp = new File(new File(home, ".gerritcodereview"), "tmp");
+      tmp = new File(getHomeDirectory(), "tmp");
     }
     if (!tmp.exists() && !tmp.mkdirs()) {
       System.err.println("warning: cannot create " + tmp.getAbsolutePath());
@@ -527,6 +512,49 @@
     }
   }
 
+  private static File locateHomeDirectory() {
+    // Try to find the user's home directory. If we can't find it
+    // return null so the JVM's default temporary directory is used
+    // instead. This is probably /tmp or /var/tmp.
+    //
+    String userHome = System.getProperty("user.home");
+    if (userHome == null || "".equals(userHome)) {
+      userHome = System.getenv("HOME");
+      if (userHome == null || "".equals(userHome)) {
+        System.err.println("warning: cannot determine home directory");
+        System.err.println("warning: using system temporary directory instead");
+        return null;
+      }
+    }
+
+    // Ensure the home directory exists. If it doesn't, try to make it.
+    //
+    final File home = new File(userHome);
+    if (!home.exists()) {
+      if (home.mkdirs()) {
+        System.err.println("warning: created " + home.getAbsolutePath());
+      } else {
+        System.err.println("warning: " + home.getAbsolutePath() + " not found");
+        System.err.println("warning: using system temporary directory instead");
+        return null;
+      }
+    }
+
+    // Use $HOME/.gerritcodereview/tmp for our temporary file area.
+    //
+    final File gerrithome = new File(home, ".gerritcodereview");
+    if (!gerrithome.exists() && !gerrithome.mkdirs()) {
+      System.err.println("warning: cannot create " + gerrithome.getAbsolutePath());
+      System.err.println("warning: using system temporary directory instead");
+      return null;
+    }
+    try {
+      return gerrithome.getCanonicalFile();
+    } catch (IOException e) {
+      return gerrithome;
+    }
+  }
+
   private GerritLauncher() {
   }
 }
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneIndexModule.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneIndexModule.java
index ef96374..583e54f 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneIndexModule.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneIndexModule.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.extensions.events.LifecycleListener;
 import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.index.ChangeSchemas;
 import com.google.gerrit.server.index.IndexCollection;
@@ -45,12 +44,7 @@
 
   @Override
   protected void configure() {
-    install(new FactoryModule() {
-      @Override
-      public void configure() {
-        factory(LuceneChangeIndex.Factory.class);
-      }
-    });
+    factory(LuceneChangeIndex.Factory.class);
     install(new IndexModule(threads));
     if (singleVersion == null && base == null) {
       install(new MultiVersionModule());
@@ -62,12 +56,7 @@
   private class MultiVersionModule extends LifecycleModule {
     @Override
     public void configure() {
-      install(new FactoryModule() {
-        @Override
-        public void configure() {
-          factory(OnlineReindexer.Factory.class);
-        }
-      });
+      factory(OnlineReindexer.Factory.class);
       listener().to(LuceneVersionManager.class);
     }
   }
diff --git a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/DiscoveryResult.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/DiscoveryResult.java
index 711f29a..37051da 100644
--- a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/DiscoveryResult.java
+++ b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/DiscoveryResult.java
@@ -25,7 +25,7 @@
     NO_PROVIDER,
 
     /** The provider was discovered, but something else failed. */
-    ERROR;
+    ERROR
   }
 
   Status status;
diff --git a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/SignInMode.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/SignInMode.java
index b6a9857..9aae6c5 100644
--- a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/SignInMode.java
+++ b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/SignInMode.java
@@ -15,5 +15,5 @@
 package com.google.gerrit.httpd.auth.openid;
 
 enum SignInMode {
-  SIGN_IN, LINK_IDENTIY, REGISTER;
+  SIGN_IN, LINK_IDENTIY, REGISTER
 }
diff --git a/gerrit-patch-jgit/BUCK b/gerrit-patch-jgit/BUCK
index 18890ac..1e0953f 100644
--- a/gerrit-patch-jgit/BUCK
+++ b/gerrit-patch-jgit/BUCK
@@ -30,3 +30,15 @@
   ],
   visibility = ['PUBLIC'],
 )
+
+java_test(
+  name = 'jgit_patch_tests',
+  srcs = glob(['src/test/java/**/*.java']),
+  deps = [
+    ':server',
+    '//lib/jgit:jgit',
+    '//lib:junit',
+  ],
+  source_under_test = [':server'],
+  visibility = ['//tools/eclipse:classpath'],
+)
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-patch-jgit/src/test/java/org/eclipse/jgit/diff/EditDeserializerTest.java
similarity index 64%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
rename to gerrit-patch-jgit/src/test/java/org/eclipse/jgit/diff/EditDeserializerTest.java
index a5371d2..f0ad62a 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-patch-jgit/src/test/java/org/eclipse/jgit/diff/EditDeserializerTest.java
@@ -12,19 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package org.eclipse.jgit.diff;
 
-import com.google.common.collect.Maps;
+import org.junit.Test;
+import static org.junit.Assert.assertNotNull;
 
-import java.util.Map;
-
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
+public class EditDeserializerTest {
+  @Test
+  public void testDiffDeserializer() {
+    assertNotNull("edit deserializer", new EditDeserializer());
   }
 }
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 6adaf32..aee16da 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
@@ -35,6 +35,7 @@
 import com.google.gerrit.pgm.http.jetty.JettyEnv;
 import com.google.gerrit.pgm.http.jetty.JettyModule;
 import com.google.gerrit.pgm.http.jetty.ProjectQoSFilter;
+import com.google.gerrit.pgm.shell.JythonShell;
 import com.google.gerrit.pgm.util.ErrorLogFile;
 import com.google.gerrit.pgm.util.GarbageCollectionLogFile;
 import com.google.gerrit.pgm.util.LogFileCompressor;
@@ -59,6 +60,7 @@
 import com.google.gerrit.server.patch.IntraLineWorkerPool;
 import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
 import com.google.gerrit.server.plugins.PluginRestApiModule;
+import com.google.gerrit.server.schema.DataSourceProvider;
 import com.google.gerrit.server.schema.SchemaVersionCheck;
 import com.google.gerrit.server.ssh.NoSshKeyCache;
 import com.google.gerrit.server.ssh.NoSshModule;
@@ -114,6 +116,9 @@
   @Option(name = "--console-log", usage = "Log to console (not $site_path/logs)")
   private boolean consoleLog;
 
+  @Option(name = "-s", usage = "Start interactive shell")
+  private boolean inspector;
+
   @Option(name = "--run-id", usage = "Cookie to store in $site_path/logs/gerrit.run")
   private String runId;
 
@@ -133,6 +138,7 @@
   private Injector httpdInjector;
   private File runFile;
   private boolean test;
+  private AbstractModule luceneModule;
 
   private Runnable serverStarted;
 
@@ -216,7 +222,16 @@
         serverStarted.run();
       }
 
-      RuntimeShutdown.waitFor();
+      if (inspector) {
+        JythonShell shell = new JythonShell();
+        shell.set("m", manager);
+        shell.set("ds", dbInjector.getInstance(DataSourceProvider.class));
+        shell.set("schk", dbInjector.getInstance(SchemaVersionCheck.class));
+        shell.set("d", this);
+        shell.run();
+      } else {
+        RuntimeShutdown.waitFor();
+      }
       return 0;
     } catch (Throwable err) {
       log.error("Unable to start daemon", err);
@@ -237,6 +252,12 @@
   }
 
   @VisibleForTesting
+  public void setLuceneModule(LuceneIndexModule m) {
+    luceneModule = m;
+    test = true;
+  }
+
+  @VisibleForTesting
   public void start() {
     if (dbInjector == null) {
       dbInjector = createDbInjector(MULTI_USER);
@@ -290,7 +311,7 @@
     AbstractModule changeIndexModule;
     switch (IndexModule.getIndexType(cfgInjector)) {
       case LUCENE:
-        changeIndexModule = new LuceneIndexModule();
+        changeIndexModule = luceneModule != null ? luceneModule : new LuceneIndexModule();
         break;
       case SOLR:
         changeIndexModule = new SolrIndexModule();
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 68e0f6a..3cfd1bb 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
@@ -56,7 +56,7 @@
   @Option(name = "--list-plugins", usage = "List available plugins")
   private boolean listPlugins;
 
-  @Option(name = "--install-plugin", usage = "Install given plugin without asking", multiValued = true)
+  @Option(name = "--install-plugin", usage = "Install given plugin without asking")
   private List<String> installPlugins;
 
   @Inject
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/PrologShell.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/PrologShell.java
index 803b702..4c66f0b 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/PrologShell.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/PrologShell.java
@@ -32,7 +32,7 @@
 import java.util.List;
 
 public class PrologShell extends AbstractProgram {
-  @Option(name = "-s", multiValued = true, metaVar = "FILE.pl", usage = "file to load")
+  @Option(name = "-s", metaVar = "FILE.pl", usage = "file to load")
   private List<String> fileName = new ArrayList<String>();
 
   @Override
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HiddenErrorHandler.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HiddenErrorHandler.java
index e086e6a..5ca53df 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HiddenErrorHandler.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HiddenErrorHandler.java
@@ -18,9 +18,9 @@
 import com.google.common.base.Strings;
 import com.google.gwtexpui.server.CacheHeaders;
 
-import org.eclipse.jetty.http.HttpHeaders;
+import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.server.AbstractHttpConnection;
+import org.eclipse.jetty.server.HttpConnection;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.handler.ErrorHandler;
 import org.slf4j.Logger;
@@ -37,8 +37,8 @@
 
   public void handle(String target, Request baseRequest,
       HttpServletRequest req, HttpServletResponse res) throws IOException {
-    AbstractHttpConnection conn = AbstractHttpConnection.getCurrentConnection();
-    conn.getRequest().setHandled(true);
+    HttpConnection conn = HttpConnection.getCurrentConnection();
+    baseRequest.setHandled(true);
     try {
       log(req);
     } finally {
@@ -46,10 +46,11 @@
     }
   }
 
-  private void reply(AbstractHttpConnection conn, HttpServletResponse res)
+  private void reply(HttpConnection conn, HttpServletResponse res)
       throws IOException {
     byte[] msg = message(conn);
-    res.setHeader(HttpHeaders.CONTENT_TYPE, "text/plain; charset=ISO-8859-1");
+    res.setHeader(HttpHeader.CONTENT_TYPE.asString(),
+        "text/plain; charset=ISO-8859-1");
     res.setContentLength(msg.length);
     try {
       CacheHeaders.setNotCacheable(res);
@@ -63,10 +64,12 @@
     }
   }
 
-  private static byte[] message(AbstractHttpConnection conn) {
-    String msg = conn.getResponse().getReason();
-    if (msg == null)
-      msg = HttpStatus.getMessage(conn.getResponse().getStatus());
+  private static byte[] message(HttpConnection conn) {
+    String msg = conn.getHttpChannel().getResponse().getReason();
+    if (msg == null) {
+      msg = HttpStatus.getMessage(conn.getHttpChannel()
+          .getResponse().getStatus());
+    }
     return msg.getBytes(Charsets.ISO_8859_1);
   }
 
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyModule.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyModule.java
index 1ae9355..b563349 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyModule.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyModule.java
@@ -15,9 +15,8 @@
 package com.google.gerrit.pgm.http.jetty;
 
 import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.inject.AbstractModule;
 
-public class JettyModule extends AbstractModule {
+public class JettyModule extends LifecycleModule {
   private final JettyEnv env;
 
   public JettyModule(final JettyEnv env) {
@@ -28,11 +27,6 @@
   protected void configure() {
     bind(JettyEnv.class).toInstance(env);
     bind(JettyServer.class);
-    install(new LifecycleModule() {
-      @Override
-      protected void configure() {
-        listener().to(JettyServer.Lifecycle.class);
-      }
-    });
+    listener().to(JettyServer.Lifecycle.class);
   }
 }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
index e1d1281b..615e11e 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
@@ -32,21 +32,24 @@
 import com.google.inject.servlet.GuiceFilter;
 import com.google.inject.servlet.GuiceServletContextListener;
 
-import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.ForwardedRequestCustomizer;
 import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
 import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
 import org.eclipse.jetty.server.handler.RequestLogHandler;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
 import org.eclipse.jetty.server.session.SessionHandler;
-import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
 import org.eclipse.jetty.servlet.DefaultServlet;
 import org.eclipse.jetty.servlet.FilterHolder;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.BlockingArrayQueue;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
@@ -131,9 +134,8 @@
       throws MalformedURLException, IOException {
     this.site = site;
 
-    httpd = new Server();
-    httpd.setConnectors(listen(cfg));
-    httpd.setThreadPool(threadPool(cfg));
+    httpd = new Server(threadPool(cfg));
+    httpd.setConnectors(listen(httpd, cfg));
 
     Handler app = makeContext(env, cfg);
     if (cfg.getBoolean("httpd", "requestLog", !reverseProxy)) {
@@ -142,15 +144,12 @@
       handler.setHandler(app);
       app = handler;
     }
-    httpd.setHandler(app);
 
+    httpd.setHandler(app);
     httpd.setStopAtShutdown(false);
-    httpd.setSendDateHeader(true);
-    httpd.setSendServerVersion(false);
-    httpd.setGracefulShutdown((int) MILLISECONDS.convert(1, SECONDS));
   }
 
-  private Connector[] listen(final Config cfg) {
+  private Connector[] listen(Server server, Config cfg) {
     // OpenID and certain web-based single-sign-on products can cause
     // some very long headers, especially in the Referer header. We
     // need to use a larger default header size to ensure we have
@@ -168,7 +167,8 @@
     for (int idx = 0; idx < listenUrls.length; idx++) {
       final URI u = listenUrls[idx];
       final int defaultPort;
-      final SelectChannelConnector c;
+      final ServerConnector c;
+      HttpConfiguration config = defaultConfig(requestHeaderSize);
 
       if (AuthType.CLIENT_SSL_CERT_LDAP.equals(authType) && ! "https".equals(u.getScheme())) {
         throw new IllegalArgumentException("Protocol '" + u.getScheme()
@@ -179,7 +179,9 @@
 
       if ("http".equals(u.getScheme())) {
         defaultPort = 80;
-        c = new SelectChannelConnector();
+        c = new ServerConnector(server, null, null, null, 0, acceptors,
+            new HttpConnectionFactory(config));
+
       } else if ("https".equals(u.getScheme())) {
         SslContextFactory ssl = new SslContextFactory();
         final File keystore = getFile(cfg, "sslkeystore", "etc/keystore");
@@ -188,7 +190,7 @@
           password = "gerrit";
         }
         ssl.setKeyStorePath(keystore.getAbsolutePath());
-        ssl.setTrustStore(keystore.getAbsolutePath());
+        ssl.setTrustStorePath(keystore.getAbsolutePath());
         ssl.setKeyStorePassword(password);
         ssl.setTrustStorePassword(password);
 
@@ -203,24 +205,27 @@
         }
 
         defaultPort = 443;
-        c = new SslSelectChannelConnector(ssl);
+
+        config.addCustomizer(new SecureRequestCustomizer());
+        c = new ServerConnector(server,
+            null, null, null, 0, acceptors,
+            new SslConnectionFactory(ssl, "http/1.1"),
+            new HttpConnectionFactory(config));
 
       } else if ("proxy-http".equals(u.getScheme())) {
         defaultPort = 8080;
-        c = new SelectChannelConnector();
-        c.setForwarded(true);
+        config.addCustomizer(new ForwardedRequestCustomizer());
+        c = new ServerConnector(server,
+            null, null, null, 0, acceptors,
+            new HttpConnectionFactory(config));
 
       } else if ("proxy-https".equals(u.getScheme())) {
         defaultPort = 8080;
-        c = new SelectChannelConnector() {
-          @Override
-          public void customize(EndPoint endpoint, Request request)
-              throws IOException {
-            request.setScheme("https");
-            super.customize(endpoint, request);
-          }
-        };
-        c.setForwarded(true);
+        config.addCustomizer(new ForwardedRequestCustomizer());
+        config.addCustomizer(new SecureRequestCustomizer());
+        c = new ServerConnector(server,
+            null, null, null, 0, acceptors,
+            new HttpConnectionFactory(config));
 
       } else {
         throw new IllegalArgumentException("Protocol '" + u.getScheme() + "' "
@@ -249,16 +254,20 @@
         throw new IllegalArgumentException("Invalid httpd.listenurl " + u, e);
       }
 
-      c.setRequestHeaderSize(requestHeaderSize);
-      c.setAcceptors(acceptors);
       c.setReuseAddress(reuseAddress);
-      c.setStatsOn(false);
-
       connectors[idx] = c;
     }
     return connectors;
   }
 
+  private HttpConfiguration defaultConfig(int requestHeaderSize) {
+    HttpConfiguration config = new HttpConfiguration();
+    config.setRequestHeaderSize(requestHeaderSize);
+    config.setSendServerVersion(false);
+    config.setSendDateHeader(true);
+    return config;
+  }
+
   static boolean isReverseProxied(final URI[] listenUrls) {
     for (URI u : listenUrls) {
       if ("http".equals(u.getScheme()) || "https".equals(u.getScheme())) {
@@ -295,11 +304,20 @@
   }
 
   private ThreadPool threadPool(Config cfg) {
-    final QueuedThreadPool pool = new QueuedThreadPool();
+    int maxThreads = cfg.getInt("httpd", null, "maxthreads", 25);
+    int minThreads = cfg.getInt("httpd", null, "minthreads", 5);
+    int maxCapacity = cfg.getInt("httpd", null, "maxqueued", 50);
+    int idleTimeout = (int)MILLISECONDS.convert(60, SECONDS);
+    QueuedThreadPool pool = new QueuedThreadPool(
+        maxThreads,
+        minThreads,
+        idleTimeout,
+        new BlockingArrayQueue<Runnable>(
+            minThreads, // capacity,
+            minThreads, // growBy,
+            maxCapacity // maxCapacity
+    ));
     pool.setName("HTTP");
-    pool.setMinThreads(cfg.getInt("httpd", null, "minthreads", 5));
-    pool.setMaxThreads(cfg.getInt("httpd", null, "maxthreads", 25));
-    pool.setMaxQueued(cfg.getInt("httpd", null, "maxqueued", 50));
     return pool;
   }
 
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/shell/JythonShell.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/shell/JythonShell.java
new file mode 100644
index 0000000..4ad91d9
--- /dev/null
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/shell/JythonShell.java
@@ -0,0 +1,221 @@
+// 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.pgm.shell;
+
+import com.google.gerrit.launcher.GerritLauncher;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Properties;
+
+public class JythonShell {
+  private static final Logger log = LoggerFactory.getLogger(JythonShell.class);
+  private static final String STARTUP_RESOURCE = "com/google/gerrit/pgm/Startup.py";
+  private static final String STARTUP_FILE = "Startup.py";
+
+  private Class<?> console;
+  private Class<?> pyObject;
+  private Class<?> pySystemState;
+  private Object shell;
+  private ArrayList <String> injectedVariables;
+
+  public JythonShell() {
+    Properties env = new Properties();
+    // Let us inspect private class members
+    env.setProperty("python.security.respectJavaAccessibility", "false");
+
+    File home = GerritLauncher.getHomeDirectory();
+    if (home != null) {
+      env.setProperty("python.cachedir", new File(home, "jythoncache").getPath());
+    }
+
+    // For package introspection and "import com.google" to work,
+    // Jython needs to inspect actual .jar files (not just classloader)
+    StringBuilder classPath = new StringBuilder();
+    final ClassLoader cl = getClass().getClassLoader();
+    if (cl instanceof java.net.URLClassLoader) {
+      URLClassLoader ucl = (URLClassLoader) cl;
+      for (URL u : ucl.getURLs()) {
+        if ("file".equals(u.getProtocol())) {
+          if (classPath.length() > 0) {
+            classPath.append(java.io.File.pathSeparatorChar);
+          }
+          classPath.append(u.getFile());
+        }
+      }
+    }
+    env.setProperty("java.class.path", classPath.toString());
+
+    console = findClass("org.python.util.InteractiveConsole");
+    pyObject = findClass("org.python.core.PyObject");
+    pySystemState = findClass("org.python.core.PySystemState");
+
+    runMethod(pySystemState, pySystemState, "initialize",
+      new Class[]  { Properties.class, Properties.class },
+      new Object[] { null, env }
+    );
+
+    try {
+      shell = console.newInstance();
+      log.info("Jython shell instance created.");
+    } catch (InstantiationException e) {
+      throw noInterpreter(e);
+    } catch (IllegalAccessException e) {
+      throw noInterpreter(e);
+    }
+    injectedVariables = new ArrayList<String>();
+    set("Shell", this);
+  }
+
+  protected Object runMethod0(Class<?> klazz, Object instance,
+    String name, Class<?>[] sig, Object[] args)
+      throws InvocationTargetException {
+    try {
+      Method m;
+      m = klazz.getMethod(name, sig);
+      return m.invoke(instance, args);
+    } catch (NoSuchMethodException e) {
+      throw cannotStart(e);
+    } catch (SecurityException e) {
+      throw cannotStart(e);
+    } catch (IllegalArgumentException e) {
+      throw cannotStart(e);
+    } catch (IllegalAccessException e) {
+      throw cannotStart(e);
+    }
+  }
+
+  protected Object runMethod(Class<?> klazz, Object instance,
+    String name, Class<?>[] sig, Object[] args) {
+    try {
+      return runMethod0(klazz, instance, name, sig, args);
+    } catch (InvocationTargetException e) {
+      throw cannotStart(e);
+    }
+  }
+
+  protected Object runInterpreter(String name, Class<?>[] sig, Object[] args) {
+    return runMethod(console, shell, name, sig, args);
+  }
+
+  protected String getDefaultBanner() {
+    return (String)runInterpreter("getDefaultBanner",
+                  new Class[] { }, new Object[] { });
+  }
+
+  protected void printInjectedVariable(String id) {
+    runInterpreter("exec",
+      new Class[]  { String.class },
+      new Object[] { "print '\"%s\" is \"%s\"' % (\"" + id + "\", " + id + ")" }
+    );
+  }
+
+  public void run() {
+    for (String key : injectedVariables) {
+      printInjectedVariable(key);
+    }
+    reload();
+    runInterpreter("interact",
+      new Class[]  { String.class, pyObject },
+      new Object[] { getDefaultBanner() +
+        " running for Gerrit " + com.google.gerrit.common.Version.getVersion(),
+        null });
+  }
+
+  public void set(String key, Object content) {
+    runInterpreter("set",
+      new Class[]  { String.class, Object.class },
+      new Object[] { key, content }
+    );
+    injectedVariables.add(key);
+  }
+
+  private static Class<?> findClass(String klazzname) {
+    try {
+      return Class.forName(klazzname);
+    } catch (ClassNotFoundException e) {
+      throw noShell("Class " + klazzname + " not found", e);
+    }
+  }
+
+  public void reload() {
+    execResource(STARTUP_RESOURCE);
+    execFile(GerritLauncher.getHomeDirectory(), STARTUP_FILE);
+  }
+
+  protected void execResource(final String p) {
+    InputStream in = JythonShell.class.getClassLoader().getResourceAsStream(p);
+    if (in != null) {
+      execStream(in, "resource " + p);
+    } else {
+      log.error("Cannot load resource " + p);
+    }
+  }
+
+  protected void execFile(final File parent, final String p) {
+    try {
+      File script = new File(parent, p);
+      if (script.canExecute()) {
+        runMethod0(console, shell, "execfile",
+          new Class[] { String.class },
+          new Object[] { script.getAbsolutePath() }
+        );
+      } else {
+        log.info("User initialization file "
+          + script.getAbsolutePath()
+          + " is not found or not executable");
+      }
+    } catch (InvocationTargetException e) {
+      log.error("Exception occured while loading file " + p + " : ", e);
+    } catch (SecurityException e) {
+      log.error("SecurityException occured while loading file " + p + " : ", e);
+    }
+  }
+
+  protected void execStream(final InputStream in, final String p) {
+    try {
+      runMethod0(console, shell, "execfile",
+        new Class[] { InputStream.class, String.class },
+        new Object[] { in, p }
+      );
+    } catch (InvocationTargetException e) {
+      log.error("Exception occured while loading " + p + " : ", e);
+    }
+  }
+
+  private static UnsupportedOperationException noShell(final String m, Throwable why) {
+    final String prefix = "Cannot create Jython shell: ";
+    final String postfix = "\n     (You might need to install jython.jar in the lib directory)";
+    return new UnsupportedOperationException(prefix + m + postfix, why);
+  }
+
+  private static UnsupportedOperationException noInterpreter(Throwable why) {
+    final String msg = "Cannot create Python interpreter";
+    return noShell(msg, why);
+  }
+
+  private static UnsupportedOperationException cannotStart(Throwable why) {
+    final String msg = "Cannot start Jython shell";
+    return new UnsupportedOperationException(msg, why);
+  }
+}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ConsoleUI.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ConsoleUI.java
index e8cf0ab..9af95ab 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ConsoleUI.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ConsoleUI.java
@@ -179,7 +179,7 @@
         }
         console.printf("       Supported options are:\n");
         for (final String v : allowedValues) {
-          console.printf("         %s\n", v.toString().toLowerCase());
+          console.printf("         %s\n", v.toLowerCase());
         }
       }
     }
diff --git a/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/Startup.py b/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/Startup.py
new file mode 100644
index 0000000..92d6e56
--- /dev/null
+++ b/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/Startup.py
@@ -0,0 +1,31 @@
+# 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.
+
+# -----------------------------------------------------------------------
+# Startup script for Gerrit Inspector - a Jython introspector
+# -----------------------------------------------------------------------
+
+import sys
+
+def help():
+  for (n, v) in vars(sys.modules['__main__']).items():
+    if not n.startswith("__") and not n in ['help', 'reload'] \
+       and str(type(v)) != "<type 'javapackage'>"             \
+       and not str(v).startswith("<module"):
+       print "\"%s\" is \"%s\"" % (n, v)
+  print
+  print "Welcome to the Gerrit Inspector"
+  print "Enter help() to see the above again, EOF to quit and stop Gerrit"
+
+help()
diff --git a/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/LibrariesTest.java b/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/LibrariesTest.java
index df1f447..9ffcf6b 100644
--- a/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/LibrariesTest.java
+++ b/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/LibrariesTest.java
@@ -21,13 +21,14 @@
 import com.google.gerrit.pgm.util.ConsoleUI;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.inject.Provider;
-
-import junit.framework.TestCase;
+import org.junit.Test;
+import static org.junit.Assert.assertNotNull;
 
 import java.io.File;
 import java.io.FileNotFoundException;
 
-public class LibrariesTest extends TestCase {
+public class LibrariesTest {
+  @Test
   public void testCreate() throws FileNotFoundException {
     final SitePaths site = new SitePaths(new File("."));
     final ConsoleUI ui = createStrictMock(ConsoleUI.class);
diff --git a/gerrit-plugin-archetype/pom.xml b/gerrit-plugin-archetype/pom.xml
index 1b33510..401f126 100644
--- a/gerrit-plugin-archetype/pom.xml
+++ b/gerrit-plugin-archetype/pom.xml
@@ -20,7 +20,7 @@
 
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-plugin-archetype</artifactId>
-  <version>2.8-SNAPSHOT</version>
+  <version>2.9-SNAPSHOT</version>
   <name>Gerrit Code Review - Plugin Archetype</name>
 
   <properties>
diff --git a/gerrit-plugin-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml b/gerrit-plugin-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
index ce8fa1a..21f508b 100644
--- a/gerrit-plugin-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
+++ b/gerrit-plugin-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -28,8 +28,12 @@
       <defaultValue>Y</defaultValue>
     </requiredProperty>
 
-    <requiredProperty key="Implementation-Vendor"/>
-    <requiredProperty key="Implementation-Url"/>
+    <requiredProperty key="Implementation-Vendor">
+      <defaultValue>Gerrit Code Review</defaultValue>
+    </requiredProperty>
+    <requiredProperty key="Implementation-Url">
+      <defaultValue>http://code.google.com/p/gerrit/</defaultValue>
+    </requiredProperty>
 
     <requiredProperty key="gerritApiType">
       <defaultValue>plugin</defaultValue>
@@ -58,6 +62,7 @@
       <directory></directory>
       <includes>
         <include>.gitignore</include>
+        <include>.settings/*</include>
         <include>LICENSE</include>
       </includes>
     </fileSet>
diff --git a/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.resources.prefs b/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..f15f85d
--- /dev/null
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,5 @@
+#Tue May 15 09:19:33 PDT 2012
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/test/java=UTF-8
+encoding//src/test/resources=UTF-8
diff --git a/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.runtime.prefs b/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000..8667cfd
--- /dev/null
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.core.prefs b/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..2f45466
--- /dev/null
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,264 @@
+#Fri Jul 16 23:39:13 PDT 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs b/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..d4218a5
--- /dev/null
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-plugin-archetype/src/main/resources/archetype-resources/pom.xml b/gerrit-plugin-archetype/src/main/resources/archetype-resources/pom.xml
index cbf8a52..cbdb9a2 100644
--- a/gerrit-plugin-archetype/src/main/resources/archetype-resources/pom.xml
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/pom.xml
@@ -1,5 +1,5 @@
 <!--
-Copyright (C) 2012 The Android Open Source Project
+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.
diff --git a/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/HttpModule.java b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/HttpModule.java
index 2840112..69949ca 100644
--- a/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/HttpModule.java
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/HttpModule.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 The Android Open Source Project
+// 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.
diff --git a/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/Module.java b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/Module.java
index 0d28349..e9668d8 100644
--- a/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/Module.java
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/Module.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 The Android Open Source Project
+// 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.
diff --git a/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/SshModule.java b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/SshModule.java
index aa15ca5..fca5112 100644
--- a/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/SshModule.java
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/SshModule.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 The Android Open Source Project
+// 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.
diff --git a/gerrit-plugin-gwt-archetype/pom.xml b/gerrit-plugin-gwt-archetype/pom.xml
index 3c4dc99..56e6c95 100644
--- a/gerrit-plugin-gwt-archetype/pom.xml
+++ b/gerrit-plugin-gwt-archetype/pom.xml
@@ -20,7 +20,7 @@
 
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-plugin-gwt-archetype</artifactId>
-  <version>2.8-SNAPSHOT</version>
+  <version>2.9-SNAPSHOT</version>
   <name>Gerrit Code Review - Web Ui GWT Plugin Archetype</name>
 
   <properties>
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml b/gerrit-plugin-gwt-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
index 78f2941..f619f91 100644
--- a/gerrit-plugin-gwt-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -18,9 +18,15 @@
   <requiredProperties>
     <requiredProperty key="pluginName"/>
 
-    <requiredProperty key="Implementation-Vendor"/>
-    <requiredProperty key="Implementation-Url"/>
-    <requiredProperty key="Gwt-Version"/>
+    <requiredProperty key="Implementation-Vendor">
+      <defaultValue>Gerrit Code Review</defaultValue>
+    </requiredProperty>
+    <requiredProperty key="Implementation-Url">
+      <defaultValue>http://code.google.com/p/gerrit/</defaultValue>
+    </requiredProperty>
+    <requiredProperty key="Gwt-Version">
+      <defaultValue>2.5.1</defaultValue>
+    </requiredProperty>
 
     <requiredProperty key="gerritApiVersion">
       <defaultValue>${defaultGerritApiVersion}</defaultValue>
@@ -49,6 +55,7 @@
       <directory></directory>
       <includes>
         <include>.gitignore</include>
+        <include>.settings/*</include>
         <include>LICENSE</include>
       </includes>
     </fileSet>
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.resources.prefs b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..f15f85d
--- /dev/null
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,5 @@
+#Tue May 15 09:19:33 PDT 2012
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/test/java=UTF-8
+encoding//src/test/resources=UTF-8
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.runtime.prefs b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000..8667cfd
--- /dev/null
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.core.prefs b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..2f45466
--- /dev/null
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,264 @@
+#Fri Jul 16 23:39:13 PDT 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..d4218a5
--- /dev/null
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/pom.xml b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/pom.xml
index 3a28c48..34912de 100644
--- a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/pom.xml
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/pom.xml
@@ -1,5 +1,5 @@
 <!--
-Copyright (C) 2012 The Android Open Source Project
+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.
@@ -71,7 +71,7 @@
         <artifactId>gwt-maven-plugin</artifactId>
         <version>${Gwt-Version}</version>
         <configuration>
-          <module>${package}.HelloPlugins</module>
+          <module>${package}.HelloPlugin</module>
           <disableClassMetadata>true</disableClassMetadata>
           <disableCastChecking>true</disableCastChecking>
           <webappDirectory>${project.build.directory}/classes/static</webappDirectory>
@@ -108,13 +108,6 @@
       <version>${Gwt-Version}</version>
       <scope>provided</scope>
     </dependency>
-
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <version>4.8.1</version>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
 
   <repositories>
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/HelloMenu.java b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/HelloMenu.java
new file mode 100644
index 0000000..546381b
--- /dev/null
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/HelloMenu.java
@@ -0,0 +1,39 @@
+// 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 ${package};
+
+import com.google.gerrit.extensions.annotations.Listen;
+import com.google.gerrit.extensions.webui.TopMenu;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@Listen
+public class HelloMenu implements TopMenu {
+  public final static String MENU_ID = "hello_open-dialog-box";
+  private final List<MenuEntry> menuEntries;
+
+  public HelloMenu() {
+    menuEntries = new ArrayList<TopMenu.MenuEntry>();
+    menuEntries.add(new MenuEntry("Hello", Collections
+        .singletonList(new MenuItem("Open Dialog Box", "", "", MENU_ID))));
+  }
+
+  @Override
+  public List<MenuEntry> getEntries() {
+    return menuEntries;
+  }
+}
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/HelloPlugins.gwt.xml b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/HelloPlugin.gwt.xml
similarity index 88%
rename from gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/HelloPlugins.gwt.xml
rename to gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/HelloPlugin.gwt.xml
index 4c70fcc..71a29d4 100644
--- a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/HelloPlugins.gwt.xml
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/HelloPlugin.gwt.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
- Copyright (C) 2012 The Android Open Source Project
+ 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.
@@ -14,7 +14,7 @@
  See the License for the specific language governing permissions and
  limitations under the License.
 -->
-<module rename-to="hello_gwt_plugins">
+<module rename-to="hello_gwt_plugin">
   <!-- Inherit the core Web Toolkit stuff.                        -->
   <inherits name="com.google.gwt.user.User"/>
   <!-- Other module inherits                                      -->
@@ -24,6 +24,6 @@
   <!-- resources to the plugin. No theme inherits lines were      -->
   <!-- added in order to make this plugin as simple as possible   -->
   <!-- Specify the app entry point class.                         -->
-  <entry-point class="${package}.client.HelloPlugins"/>
+  <entry-point class="${package}.client.HelloPlugin"/>
   <stylesheet src="hello.css"/>
 </module>
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/MyExtension.java b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/MyExtension.java
index ebdbb26..cf0f52d 100644
--- a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/MyExtension.java
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/MyExtension.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Google
+// 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.
@@ -20,6 +20,6 @@
 @Listen
 public class MyExtension extends GwtPlugin {
   public MyExtension() {
-    super("hello_gwt_plugins");
+    super("hello_gwt_plugin");
   }
 }
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/client/HelloPlugins.java b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/client/HelloPlugin.java
similarity index 70%
rename from gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/client/HelloPlugins.java
rename to gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/client/HelloPlugin.java
index 5584d85..24b8136 100644
--- a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/client/HelloPlugins.java
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/client/HelloPlugin.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Google Inc
+// 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.
@@ -14,33 +14,23 @@
 
 package ${package}.client;
 
-import com.google.gerrit.client.Plugin;
+import com.google.gerrit.plugin.client.Plugin;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.DialogBox;
-import com.google.gwt.user.client.ui.Image;
 import com.google.gwt.user.client.ui.RootPanel;
 import com.google.gwt.user.client.ui.VerticalPanel;
 
+import ${package}.HelloMenu;
+
 /**
- * HelloWorld Plugins.
+ * HelloWorld Plugin.
  */
-public class HelloPlugins extends Plugin {
+public class HelloPlugin extends Plugin {
 
   @Override
   public void onModuleLoad() {
-    Image img = new Image("http://code.google.com/webtoolkit/logo-185x175.png");
-    Button button = new Button("Click me");
-
-    VerticalPanel vPanel = new VerticalPanel();
-    vPanel.setWidth("100%");
-    vPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER);
-    vPanel.add(img);
-    vPanel.add(button);
-
-    RootPanel.get().add(vPanel);
-
     // Create the dialog box
     final DialogBox dialogBox = new DialogBox();
 
@@ -62,11 +52,14 @@
     // Set the contents of the Widget
     dialogBox.setWidget(dialogVPanel);
 
-    button.addClickHandler(new ClickHandler() {
-      public void onClick(ClickEvent event) {
-        dialogBox.center();
-        dialogBox.show();
-      }
-    });
+    RootPanel rootPanel = RootPanel.get(HelloMenu.MENU_ID);
+    rootPanel.getElement().removeAttribute("href");
+    rootPanel.addDomHandler(new ClickHandler() {
+        @Override
+        public void onClick(ClickEvent event) {
+          dialogBox.center();
+          dialogBox.show();
+        }
+    }, ClickEvent.getType());
   }
 }
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/public/gwt-hello-gadgets-igoogle-thumb.png b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/public/gwt-hello-gadgets-igoogle-thumb.png
deleted file mode 100644
index e970774..0000000
--- a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/public/gwt-hello-gadgets-igoogle-thumb.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/public/gwt-hello-gadgets-igoogle.png b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/public/gwt-hello-gadgets-igoogle.png
deleted file mode 100644
index c041149..0000000
--- a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/public/gwt-hello-gadgets-igoogle.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/public/hello.css b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/public/hello.css
index 73bf5c6..13c5177 100644
--- a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/public/hello.css
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/java/public/hello.css
@@ -101,3 +101,6 @@
   zoom: 1;
 }
 
+#hello_open-dialog-box {
+  cursor: pointer;
+}
diff --git a/gerrit-plugin-gwtui/BUCK b/gerrit-plugin-gwtui/BUCK
new file mode 100644
index 0000000..58eda5c
--- /dev/null
+++ b/gerrit-plugin-gwtui/BUCK
@@ -0,0 +1,20 @@
+SRC = 'src/main/java/com/google/gerrit/'
+
+gwt_module(
+  name = 'client',
+  srcs = glob([SRC + '**/*.java']),
+  gwtxml = SRC + 'Plugin.gwt.xml',
+  resources = glob(['src/main/resources/**/*']),
+  deps = [
+    '//lib/gwt:user',
+    '//lib/gwt:dev',
+  ],
+  visibility = ['PUBLIC'],
+)
+
+java_library(
+  name = 'src',
+  srcs = [],
+  resources = glob(['src/main/**/*']),
+  visibility = ['PUBLIC'],
+)
diff --git a/gerrit-plugin-gwtui/pom.xml b/gerrit-plugin-gwtui/pom.xml
deleted file mode 100644
index 3c9e2ca..0000000
--- a/gerrit-plugin-gwtui/pom.xml
+++ /dev/null
@@ -1,161 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2012 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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-
-  <groupId>com.google.gerrit</groupId>
-  <artifactId>gerrit-plugin-gwtui</artifactId>
-  <version>2.8-SNAPSHOT</version>
-  <name>Gerrit Code Review - Plugin GWT UI</name>
-
-  <description>
-    API for UI plugins to build with GWT and integrate with Gerrit
-  </description>
-
-  <dependencies>
-    <dependency>
-      <groupId>com.google.gwt</groupId>
-      <artifactId>gwt-user</artifactId>
-      <version>2.5.1</version>
-    </dependency>
-
-    <dependency>
-      <groupId>com.google.gwt</groupId>
-      <artifactId>gwt-dev</artifactId>
-      <version>2.5.1</version>
-    </dependency>
-  </dependencies>
-
-   <build>
-     <pluginManagement>
-       <plugins>
-         <plugin>
-           <groupId>org.eclipse.m2e</groupId>
-           <artifactId>lifecycle-mapping</artifactId>
-           <version>1.0.0</version>
-           <configuration>
-             <lifecycleMappingMetadata>
-               <pluginExecutions>
-                 <pluginExecution>
-                   <pluginExecutionFilter>
-                     <groupId>org.codehaus.mojo</groupId>
-                     <artifactId>gwt-maven-plugin</artifactId>
-                     <versionRange>[2.5.0,)</versionRange>
-                     <goals>
-                       <goal>resources</goal>
-                       <goal>compile</goal>
-                       <goal>i18n</goal>
-                       <goal>generateAsync</goal>
-                     </goals>
-                   </pluginExecutionFilter>
-                   <action>
-                     <execute />
-                   </action>
-                 </pluginExecution>
-                 <pluginExecution>
-                   <pluginExecutionFilter>
-                     <groupId>org.apache.maven.plugins</groupId>
-                     <artifactId>maven-war-plugin</artifactId>
-                     <versionRange>[2.1.1,)</versionRange>
-                     <goals>
-                       <goal>exploded</goal>
-                     </goals>
-                   </pluginExecutionFilter>
-                   <action>
-                     <execute />
-                   </action>
-                 </pluginExecution>
-               </pluginExecutions>
-             </lifecycleMappingMetadata>
-          </configuration>
-        </plugin>
-      </plugins>
-    </pluginManagement>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-source-plugin</artifactId>
-        <executions>
-          <execution>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>gwt-maven-plugin</artifactId>
-        <configuration>
-          <module>com.google.gerrit.Plugin</module>
-          <disableClassMetadata>true</disableClassMetadata>
-          <disableCastChecking>true</disableCastChecking>
-        </configuration>
-        <executions>
-          <execution>
-            <goals>
-              <goal>resources</goal>
-              <goal>compile</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-antrun-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>unpack-sources</id>
-            <phase>package</phase>
-            <configuration>
-              <tasks>
-                <unzip src="${project.build.directory}/${project.artifactId}-${project.version}-sources.jar" dest="${project.build.directory}/unpack_sources" />
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-javadoc-plugin</artifactId>
-        <configuration>
-          <sourcepath>${project.build.directory}/unpack_sources</sourcepath>
-          <encoding>ISO-8859-1</encoding>
-          <quiet>true</quiet>
-          <detectOfflineLinks>false</detectOfflineLinks>
-        </configuration>
-        <executions>
-          <execution>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-            <phase>package</phase>
-          </execution>
-        </executions>
-      </plugin>
-
-    </plugins>
-  </build>
-</project>
-
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/Plugin.gwt.xml b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/Plugin.gwt.xml
index 03edf67..1e2280a 100644
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/Plugin.gwt.xml
+++ b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/Plugin.gwt.xml
@@ -14,10 +14,10 @@
  limitations under the License.
 -->
 <module>
-  <define-linker name="gerrit_plugin" class="com.google.gerrit.linker.GerritPluginLinker"/>
+  <define-linker name="gerrit_plugin" class="com.google.gerrit.plugin.linker.GerritPluginLinker"/>
   <add-linker name="gerrit_plugin"/>
-  <generate-with class="com.google.gerrit.rebind.PluginGenerator">
-    <when-type-assignable class="com.google.gerrit.client.Plugin"/>
+  <generate-with class="com.google.gerrit.plugin.rebind.PluginGenerator">
+    <when-type-assignable class="com.google.gerrit.plugin.client.Plugin"/>
   </generate-with>
-  <source path="client"/>
+  <source path="plugin/client"/>
 </module>
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/client/Plugin.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/Plugin.java
similarity index 95%
rename from gerrit-plugin-gwtui/src/main/java/com/google/gerrit/client/Plugin.java
rename to gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/Plugin.java
index 1291b79..b5f7175 100644
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/client/Plugin.java
+++ b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/Plugin.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.client;
+package com.google.gerrit.plugin.client;
 
 import com.google.gwt.core.client.EntryPoint;
 
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/NativeString.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/NativeString.java
new file mode 100644
index 0000000..fe65857
--- /dev/null
+++ b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/NativeString.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.plugin.client.rpc;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+/** Wraps a String that was returned from a JSON API. */
+public class NativeString extends JavaScriptObject {
+  private static final JavaScriptObject TYPE = init();
+
+  private static final native JavaScriptObject init()
+  /*-{ return $wnd.Gerrit.JsonString }-*/;
+
+  public final native String asString()
+  /*-{ return this.get(); }-*/;
+
+  public static final
+  AsyncCallback<NativeString> unwrap(final AsyncCallback<String> cb) {
+    return new AsyncCallback<NativeString>() {
+      @Override
+      public void onSuccess(NativeString result) {
+        cb.onSuccess(result != null ? result.asString() : null);
+      }
+
+      @Override
+      public void onFailure(Throwable caught) {
+        cb.onFailure(caught);
+      }
+    };
+  }
+
+  public static final boolean is(JavaScriptObject o) {
+    return is(TYPE, o);
+  }
+
+  private static final native boolean is(JavaScriptObject T, JavaScriptObject o)
+  /*-{ return o instanceof T }-*/;
+
+  protected NativeString() {
+  }
+}
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/Natives.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/Natives.java
new file mode 100644
index 0000000..6f5b136
--- /dev/null
+++ b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/Natives.java
@@ -0,0 +1,71 @@
+// Copyright (C) 2012 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.plugin.client.rpc;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.json.client.JSONObject;
+
+import java.util.AbstractList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+public class Natives {
+  /**
+   * Get the names of defined properties on the object. The returned set
+   * iterates in the native iteration order, which may match the source order.
+   */
+  public static Set<String> keys(JavaScriptObject obj) {
+    if (obj != null) {
+      return new JSONObject(obj).keySet();
+    }
+    return Collections.emptySet();
+  }
+
+  public static <T extends JavaScriptObject> List<T> asList(
+      final JsArray<T> arr) {
+    if (arr == null) {
+      return null;
+    }
+    return new AbstractList<T>() {
+      @Override
+      public T set(int index, T element) {
+        T old = arr.get(index);
+        arr.set(index, element);
+        return old;
+      }
+
+      @Override
+      public T get(int index) {
+        return arr.get(index);
+      }
+
+      @Override
+      public int size() {
+        return arr.length();
+      }
+    };
+  }
+
+  public static <T extends JavaScriptObject> JsArray<T> arrayOf(T element) {
+    JsArray<T> arr = JavaScriptObject.createArray().cast();
+    arr.push(element);
+    return arr;
+  }
+
+  private Natives() {
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/NoContent.java
similarity index 64%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/NoContent.java
index a5371d2..55744d5 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/NoContent.java
@@ -12,19 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.plugin.client.rpc;
 
-import com.google.common.collect.Maps;
+import com.google.gwt.core.client.JavaScriptObject;
 
-import java.util.Map;
-
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
+public class NoContent extends JavaScriptObject {
+  protected NoContent() {
   }
 }
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/RestApi.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/RestApi.java
new file mode 100644
index 0000000..57a23e0
--- /dev/null
+++ b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/RestApi.java
@@ -0,0 +1,171 @@
+// 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.plugin.client.rpc;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.http.client.URL;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+public class RestApi {
+  private final StringBuilder path;
+  private boolean hasQueryParams;
+
+  public RestApi(String name) {
+    path = new StringBuilder();
+    path.append(name);
+  }
+
+  public RestApi view(String name) {
+    return idRaw(name);
+  }
+
+  public RestApi view(String pluginName, String name) {
+    return idRaw(pluginName + "~" + name);
+  }
+
+  public RestApi id(String id) {
+    return idRaw(URL.encodeQueryString(id));
+  }
+
+  public RestApi id(int id) {
+    return idRaw(Integer.toString(id));
+  }
+
+  public RestApi idRaw(String name) {
+    if (hasQueryParams) {
+      throw new IllegalStateException();
+    }
+    if (path.charAt(path.length() - 1) != '/') {
+      path.append('/');
+    }
+    path.append(name);
+    return this;
+  }
+
+  public RestApi addParameter(String name, String value) {
+    return addParameterRaw(name, URL.encodeQueryString(value));
+  }
+
+  public RestApi addParameter(String name, String... value) {
+    for (String val : value) {
+      addParameter(name, val);
+    }
+    return this;
+  }
+
+  public RestApi addParameterTrue(String name) {
+    return addParameterRaw(name, null);
+  }
+
+  public RestApi addParameter(String name, boolean value) {
+    return addParameterRaw(name, value ? "t" : "f");
+  }
+
+  public RestApi addParameter(String name, int value) {
+    return addParameterRaw(name, String.valueOf(value));
+  }
+
+  public RestApi addParameter(String name, Enum<?> value) {
+    return addParameterRaw(name, value.name());
+  }
+
+  public RestApi addParameterRaw(String name, String value) {
+    if (hasQueryParams) {
+      path.append("&");
+    } else {
+      path.append("?");
+      hasQueryParams = true;
+    }
+    path.append(name);
+    if (value != null) {
+      path.append("=").append(value);
+    }
+    return this;
+  }
+
+  public String path() {
+    return path.toString();
+  }
+
+  public <T extends JavaScriptObject>
+  void get(AsyncCallback<T> cb) {
+    get(path(), wrap(cb));
+  }
+
+  public void getString(AsyncCallback<String> cb) {
+    get(NativeString.unwrap(cb));
+  }
+
+  private native static void get(String p, JavaScriptObject r)
+  /*-{ $wnd.Gerrit.get(p, r) }-*/;
+
+  public <T extends JavaScriptObject>
+  void put(AsyncCallback<T> cb) {
+    put(path(), wrap(cb));
+  }
+
+  private native static void put(String p, JavaScriptObject r)
+  /*-{ $wnd.Gerrit.put(p, r) }-*/;
+
+  public <T extends JavaScriptObject>
+  void put(String content, AsyncCallback<T> cb) {
+    put(path(), content, wrap(cb));
+  }
+
+  private native static
+  void put(String p, String c, JavaScriptObject r)
+  /*-{ $wnd.Gerrit.put(p, c, r) }-*/;
+
+  public <T extends JavaScriptObject>
+  void put(JavaScriptObject content, AsyncCallback<T> cb) {
+    put(path(), content, wrap(cb));
+  }
+
+  private native static
+  void put(String p, JavaScriptObject c, JavaScriptObject r)
+  /*-{ $wnd.Gerrit.put(p, c, r) }-*/;
+
+  public <T extends JavaScriptObject>
+  void post(String content, AsyncCallback<T> cb) {
+    post(path(), content, wrap(cb));
+  }
+
+  private native static
+  void post(String p, String c, JavaScriptObject r)
+  /*-{ $wnd.Gerrit.post(p, c, r) }-*/;
+
+  public <T extends JavaScriptObject>
+  void post(JavaScriptObject content, AsyncCallback<T> cb) {
+    post(path(), content, wrap(cb));
+  }
+
+  private native static
+  void post(String p, JavaScriptObject c, JavaScriptObject r)
+  /*-{ $wnd.Gerrit.post(p, c, r) }-*/;
+
+  public void delete(AsyncCallback<NoContent> cb) {
+    delete(path(), wrap(cb));
+  }
+
+  private native static void delete(String p, JavaScriptObject r)
+  /*-{ '$wnd.Gerrit.delete'(p, r) }-*/;
+
+  private native static <T extends JavaScriptObject>
+  JavaScriptObject wrap(AsyncCallback<T> b) /*-{
+    return function(r) {
+      b.@com.google.gwt.user.client.rpc.AsyncCallback::onSuccess(Ljava/lang/Object;)(r)
+    }
+  }-*/;
+}
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/linker/GerritPluginLinker.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/linker/GerritPluginLinker.java
similarity index 96%
rename from gerrit-plugin-gwtui/src/main/java/com/google/gerrit/linker/GerritPluginLinker.java
rename to gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/linker/GerritPluginLinker.java
index e50334e..18f6e54 100644
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/linker/GerritPluginLinker.java
+++ b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/linker/GerritPluginLinker.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.linker;
+package com.google.gerrit.plugin.linker;
 
 import com.google.gwt.core.ext.LinkerContext;
 import com.google.gwt.core.linker.CrossSiteIframeLinker;
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/rebind/PluginGenerator.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/rebind/PluginGenerator.java
similarity index 98%
rename from gerrit-plugin-gwtui/src/main/java/com/google/gerrit/rebind/PluginGenerator.java
rename to gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/rebind/PluginGenerator.java
index 37c3e96..8278280 100644
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/rebind/PluginGenerator.java
+++ b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/rebind/PluginGenerator.java
@@ -13,7 +13,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.rebind;
+package com.google.gerrit.plugin.rebind;
 
 import java.io.PrintWriter;
 
diff --git a/gerrit-plugin-js-archetype/pom.xml b/gerrit-plugin-js-archetype/pom.xml
index 681d7a9..eb32b11 100644
--- a/gerrit-plugin-js-archetype/pom.xml
+++ b/gerrit-plugin-js-archetype/pom.xml
@@ -20,7 +20,7 @@
 
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-plugin-js-archetype</artifactId>
-  <version>2.8-SNAPSHOT</version>
+  <version>2.9-SNAPSHOT</version>
   <name>Gerrit Code Review - Web UI JavaScript Plugin Archetype</name>
 
   <properties>
diff --git a/gerrit-plugin-js-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml b/gerrit-plugin-js-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
index 054caae..e4978dd 100644
--- a/gerrit-plugin-js-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
+++ b/gerrit-plugin-js-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -18,8 +18,12 @@
   <requiredProperties>
     <requiredProperty key="pluginName"/>
 
-    <requiredProperty key="Implementation-Vendor"/>
-    <requiredProperty key="Implementation-Url"/>
+    <requiredProperty key="Implementation-Vendor">
+      <defaultValue>Gerrit Code Review</defaultValue>
+    </requiredProperty>
+    <requiredProperty key="Implementation-Url">
+      <defaultValue>http://code.google.com/p/gerrit/</defaultValue>
+    </requiredProperty>
 
     <requiredProperty key="gerritApiType">
       <defaultValue>js</defaultValue>
@@ -55,6 +59,7 @@
       <directory></directory>
       <includes>
         <include>.gitignore</include>
+        <include>.settings/*</include>
         <include>LICENSE</include>
       </includes>
     </fileSet>
diff --git a/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.resources.prefs b/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..f15f85d
--- /dev/null
+++ b/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,5 @@
+#Tue May 15 09:19:33 PDT 2012
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/test/java=UTF-8
+encoding//src/test/resources=UTF-8
diff --git a/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.runtime.prefs b/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000..8667cfd
--- /dev/null
+++ b/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.core.prefs b/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..2f45466
--- /dev/null
+++ b/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,264 @@
+#Fri Jul 16 23:39:13 PDT 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs b/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..d4218a5
--- /dev/null
+++ b/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/pom.xml b/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/pom.xml
index 85de7b5..207e49a 100644
--- a/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/pom.xml
+++ b/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/pom.xml
@@ -1,5 +1,5 @@
 <!--
-Copyright (C) 2012 The Android Open Source Project
+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.
diff --git a/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/src/main/java/MyJsExtension.java b/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/src/main/java/MyJsExtension.java
index bec914dd..da80a83 100644
--- a/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/src/main/java/MyJsExtension.java
+++ b/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/src/main/java/MyJsExtension.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 The Android Open Source Project
+// 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.
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
index 5cfa5e1..5dc2a08 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
@@ -56,7 +56,7 @@
  */
 public final class Account {
   public static enum FieldName {
-    FULL_NAME, USER_NAME, REGISTER_NEW_EMAIL;
+    FULL_NAME, USER_NAME, REGISTER_NEW_EMAIL
   }
 
   public static final String USER_NAME_PATTERN_FIRST = "[a-zA-Z]";
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 6cc83e5..efdc92b 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,12 +27,12 @@
 
   /** Preferred scheme type to download a change. */
   public static enum DownloadScheme {
-    ANON_GIT, ANON_HTTP, HTTP, SSH, REPO_DOWNLOAD, DEFAULT_DOWNLOADS;
+    ANON_GIT, ANON_HTTP, HTTP, SSH, REPO_DOWNLOAD, DEFAULT_DOWNLOADS
   }
 
   /** Preferred method to download a change. */
   public static enum DownloadCommand {
-    REPO_DOWNLOAD, PULL, CHECKOUT, CHERRY_PICK, FORMAT_PATCH, DEFAULT_DOWNLOADS;
+    REPO_DOWNLOAD, PULL, CHECKOUT, CHERRY_PICK, FORMAT_PATCH, DEFAULT_DOWNLOADS
   }
 
   public static enum DateFormat {
@@ -69,7 +69,7 @@
     COLLAPSE_ALL,
     EXPAND_MOST_RECENT,
     EXPAND_RECENT,
-    EXPAND_ALL;
+    EXPAND_ALL
   }
 
   public static enum DiffView {
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroup.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroup.java
index ea9c52d..a4846ac 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroup.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroup.java
@@ -142,7 +142,7 @@
      * who is a member of the owner group. These groups are not treated special
      * in the code.
      */
-    INTERNAL;
+    INTERNAL
   }
 
   /** Common UUID assigned to the "Project Owners" placeholder group. */
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AuthType.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AuthType.java
index b615fc5..6af9610 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AuthType.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AuthType.java
@@ -80,5 +80,5 @@
   CUSTOM_EXTENSION,
 
   /** Development mode to enable becoming anyone you want. */
-  DEVELOPMENT_BECOME_ANY_ACCOUNT;
+  DEVELOPMENT_BECOME_ANY_ACCOUNT
 }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java
index f3cf471..3b5a1aa 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java
@@ -74,7 +74,7 @@
 
     MERGE_ALWAYS,
 
-    CHERRY_PICK;
+    CHERRY_PICK
   }
 
   public static enum State {
@@ -82,13 +82,13 @@
 
     READ_ONLY,
 
-    HIDDEN;
+    HIDDEN
   }
 
   public static enum InheritableBoolean {
     TRUE,
     FALSE,
-    INHERIT;
+    INHERIT
   }
 
   protected NameKey name;
diff --git a/gerrit-server/BUCK b/gerrit-server/BUCK
index 1db7555..8b3f531 100644
--- a/gerrit-server/BUCK
+++ b/gerrit-server/BUCK
@@ -1,15 +1,28 @@
+CONSTANTS_SRC = [
+  'src/main/java/com/google/gerrit/server/documentation/Constants.java',
+]
+
 SRCS = glob([
-  'src/main/java/**/*.java',
-  'src/test/java/com/google/gerrit/server/project/Util.java'
-])
+    'src/main/java/**/*.java',
+    'src/test/java/com/google/gerrit/server/project/Util.java',
+  ],
+  excludes = CONSTANTS_SRC,
+)
 RESOURCES =  glob(['src/main/resources/**/*'])
 
+java_library2(
+  name = 'constants',
+  srcs = CONSTANTS_SRC,
+  visibility = ['PUBLIC'],
+)
+
 # TODO(sop) break up gerrit-server java_library(), its too big
 java_library2(
   name = 'server',
   srcs = SRCS,
   resources = RESOURCES,
   deps = [
+    ':constants',
     '//gerrit-antlr:query_exception',
     '//gerrit-antlr:query_parser',
     '//gerrit-common:server',
@@ -48,6 +61,9 @@
     '//lib/joda:joda-time',
     '//lib/log:api',
     '//lib/prolog:prolog-cafe',
+    '//lib/lucene:analyzers-common',
+    '//lib/lucene:core',
+    '//lib/lucene:query-parser',
   ],
   compile_deps = [
     '//lib/bouncycastle:bcprov',
@@ -129,6 +145,7 @@
     '//gerrit-common:server',
     '//gerrit-extension-api:api',
     '//gerrit-reviewdb:server',
+    '//lib:args4j',
     '//lib:easymock',
     '//lib:guava',
     '//lib:gwtorm',
diff --git a/gerrit-server/src/main/java/com/google/gerrit/lifecycle/LifecycleModule.java b/gerrit-server/src/main/java/com/google/gerrit/lifecycle/LifecycleModule.java
index 04682f5..a22e6da 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/lifecycle/LifecycleModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/lifecycle/LifecycleModule.java
@@ -1,7 +1,7 @@
 package com.google.gerrit.lifecycle;
 
 import com.google.gerrit.extensions.events.LifecycleListener;
-import com.google.inject.AbstractModule;
+import com.google.gerrit.server.config.FactoryModule;
 import com.google.inject.Singleton;
 import com.google.inject.binder.LinkedBindingBuilder;
 import com.google.inject.internal.UniqueAnnotations;
@@ -9,7 +9,7 @@
 import java.lang.annotation.Annotation;
 
 /** Module to support registering a unique LifecyleListener. */
-public abstract class LifecycleModule extends AbstractModule {
+public abstract class LifecycleModule extends FactoryModule {
   /**
    * Create a unique listener binding.
    * <p>
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java
index a0196fd..4ab3442 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java
@@ -64,7 +64,7 @@
   }
 
   public static enum Status {
-    NO_RULES, COMPILED;
+    NO_RULES, COMPILED
   }
 
   private final File ruleDir;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/AccessPath.java b/gerrit-server/src/main/java/com/google/gerrit/server/AccessPath.java
index 31df875..cb720c8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/AccessPath.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/AccessPath.java
@@ -32,5 +32,5 @@
   SSH_COMMAND,
 
   /** Access from a Git client using any Git protocol. */
-  GIT;
+  GIT
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/DefaultFileExtensionRegistry.java b/gerrit-server/src/main/java/com/google/gerrit/server/DefaultFileExtensionRegistry.java
new file mode 100644
index 0000000..15062ac
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/DefaultFileExtensionRegistry.java
@@ -0,0 +1,104 @@
+// 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;
+
+import com.google.common.collect.ImmutableMap;
+
+import eu.medsea.mimeutil.MimeType;
+import eu.medsea.mimeutil.MimeUtil;
+import eu.medsea.mimeutil.detector.MimeDetector;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Collections;
+
+public class DefaultFileExtensionRegistry extends MimeDetector {
+  private static final MimeType INI = newMimeType("text/x-ini", 2);
+  private static final MimeType PYTHON = newMimeType("text/x-python", 2);
+
+  private static final ImmutableMap<String, MimeType> TYPES = ImmutableMap.of(
+      ".gitmodules", INI,
+      "project.config", INI,
+      "BUCK", PYTHON,
+      "defs", newMimeType(PYTHON.toString(), 1),
+      "go", newMimeType("text/x-go", 1));
+
+  private static MimeType newMimeType(String type, final int specificity) {
+    return new MimeType(type) {
+      private static final long serialVersionUID = 1L;
+
+      @Override
+      public int getSpecificity() {
+        return specificity;
+      }
+    };
+  }
+
+  static {
+    for (MimeType type : TYPES.values()) {
+      MimeUtil.addKnownMimeType(type);
+    }
+  }
+
+  @Override
+  public String getDescription() {
+    return getClass().getName();
+  }
+
+  @Override
+  protected Collection<MimeType> getMimeTypesFileName(String name) {
+    int s = name.lastIndexOf('/');
+    if (s >= 0) {
+      name = name.substring(s + 1);
+    }
+
+    MimeType type = TYPES.get(name);
+    if (type != null) {
+      return Collections.singletonList(type);
+    }
+
+    int d = name.lastIndexOf('.');
+    if (0 < d) {
+      type = TYPES.get(name.substring(d + 1));
+      if (type != null) {
+        return Collections.singletonList(type);
+      }
+    }
+
+    return Collections.emptyList();
+  }
+
+  @Override
+  protected Collection<MimeType> getMimeTypesFile(File file) {
+    return getMimeTypesFileName(file.getName());
+  }
+
+  @Override
+  protected Collection<MimeType> getMimeTypesURL(URL url) {
+    return getMimeTypesFileName(url.getPath());
+  }
+
+  @Override
+  protected Collection<MimeType> getMimeTypesInputStream(InputStream arg0) {
+    return Collections.emptyList();
+  }
+
+  @Override
+  protected Collection<MimeType> getMimeTypesByteArray(byte[] arg0) {
+    return Collections.emptyList();
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/MimeUtilFileTypeRegistry.java b/gerrit-server/src/main/java/com/google/gerrit/server/MimeUtilFileTypeRegistry.java
index b271d6a..ff46b00 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/MimeUtilFileTypeRegistry.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/MimeUtilFileTypeRegistry.java
@@ -54,13 +54,13 @@
     if (HostPlatform.isWin32()) {
       register("eu.medsea.mimeutil.detector.WindowsRegistryMimeDetector");
     }
+    register(DefaultFileExtensionRegistry.class.getName());
   }
 
   private void register(String name) {
     mimeUtil.registerMimeDetector(name);
   }
 
-
   /**
    * Get specificity of mime types with generic types forced to low values
    *
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/StringUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/StringUtil.java
index fe1072d..2133dfb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/StringUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/StringUtil.java
@@ -22,7 +22,7 @@
    * corresponds to its ASCII value, i.e. the string representation of
    * ASCII 0 is found in the first element of this array.
    */
-  static String[] NON_PRINTABLE_CHARS =
+  private static final String[] NON_PRINTABLE_CHARS =
     { "\\x00", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\a",
       "\\b",   "\\t",   "\\n",   "\\v",   "\\f",   "\\r",   "\\x0e", "\\x0f",
       "\\x10", "\\x11", "\\x12", "\\x13", "\\x14", "\\x15", "\\x16", "\\x17",
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountDirectory.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountDirectory.java
index a4881a4..4dc9d79 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountDirectory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountDirectory.java
@@ -34,7 +34,7 @@
     AVATARS,
 
     /** Unique user identity to login to Gerrit, may be deprecated. */
-    USERNAME;
+    USERNAME
   }
 
   public abstract void fillAccountInfo(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountVisibility.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountVisibility.java
index 7452da3..7ee8db6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountVisibility.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountVisibility.java
@@ -29,5 +29,5 @@
    * Other accounts are not visible to the given user unless they are explicitly
    * collaborating on a change.
    */
-  NONE;
+  NONE
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java
index 2cff009..0b40c81 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java
@@ -19,9 +19,7 @@
 import com.google.gerrit.common.errors.InvalidSshKeyException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.RawInput;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.AccountSshKey;
@@ -57,8 +55,7 @@
 
   @Override
   public Response<SshKeyInfo> apply(AccountResource rsrc, Input input)
-      throws AuthException, MethodNotAllowedException, BadRequestException,
-      ResourceConflictException, OrmException, IOException {
+      throws AuthException, BadRequestException, OrmException, IOException {
     if (self.get() != rsrc.getUser()
         && !self.get().getCapabilities().canAdministrateServer()) {
       throw new AuthException("not allowed to add SSH keys");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java
index 340746e..3b03c3a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java
@@ -89,7 +89,7 @@
   }
 
   @Override
-  public Object apply(TopLevelResource rsrc, Input input)
+  public Response<AccountInfo> apply(TopLevelResource rsrc, Input input)
       throws BadRequestException, ResourceConflictException,
       UnprocessableEntityException, OrmException {
     if (input == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java
index 4fda74c..9a5a864 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java
@@ -78,8 +78,8 @@
   }
 
   @Override
-  public Object apply(AccountResource rsrc, Input input) throws AuthException,
-      BadRequestException, ResourceConflictException,
+  public Response<EmailInfo> apply(AccountResource rsrc, Input input)
+      throws AuthException, BadRequestException, ResourceConflictException,
       ResourceNotFoundException, OrmException, EmailException,
       MethodNotAllowedException {
     if (self.get() != rsrc.getUser()
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java
index d44bc2c..4382655 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java
@@ -43,7 +43,7 @@
   }
 
   @Override
-  public Object apply(AccountResource rsrc, Input input)
+  public Response<?> apply(AccountResource rsrc, Input input)
       throws ResourceNotFoundException, OrmException {
     Account a = dbProvider.get().accounts().get(rsrc.getUser().getAccountId());
     if (a == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java
index 4b38b9f..b38c49b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java
@@ -48,7 +48,7 @@
   }
 
   @Override
-  public Object apply(AccountResource.Email rsrc, Input input)
+  public Response<?> apply(AccountResource.Email rsrc, Input input)
       throws AuthException, ResourceNotFoundException,
       ResourceConflictException, MethodNotAllowedException, OrmException {
     if (self.get() != rsrc.getUser()
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java
index cf60df1..bbba48a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java
@@ -40,7 +40,7 @@
   }
 
   @Override
-  public Object apply(AccountResource.SshKey rsrc, Input input)
+  public Response<?> apply(AccountResource.SshKey rsrc, Input input)
       throws OrmException {
     dbProvider.get().accountSshKeys()
         .deleteKeys(Collections.singleton(rsrc.getSshKey().getKey()));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetActive.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetActive.java
index 76c7ddb..c042e18 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetActive.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetActive.java
@@ -14,16 +14,16 @@
 
 package com.google.gerrit.server.account;
 
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 
 public class GetActive implements RestReadView<AccountResource> {
   @Override
-  public Object apply(AccountResource rsrc) throws ResourceNotFoundException {
+  public Object apply(AccountResource rsrc) {
     if (rsrc.getUser().getAccount().isActive()) {
-      return Response.ok("");
+      return BinaryResult.create("ok\n");
     }
-    throw new ResourceNotFoundException();
+    return Response.none();
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java
index 615d09e..03ba94f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java
@@ -36,7 +36,6 @@
 import com.google.gerrit.extensions.config.CapabilityDefinition;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.CurrentUser;
@@ -59,7 +58,7 @@
   @Option(name = "--format", usage = "(deprecated) output format")
   private OutputFormat format;
 
-  @Option(name = "-q", metaVar = "CAP", multiValued = true, usage = "Capability to inspect")
+  @Option(name = "-q", metaVar = "CAP", usage = "Capability to inspect")
   void addQuery(String name) {
     if (query == null) {
       query = Sets.newHashSet();
@@ -79,8 +78,7 @@
   }
 
   @Override
-  public Object apply(AccountResource resource)
-      throws BadRequestException, Exception {
+  public Object apply(AccountResource resource) throws AuthException {
     if (self.get() != resource.getUser()
         && !self.get().getCapabilities().canAdministrateServer()) {
       throw new AuthException("restricted to administrator");
@@ -178,7 +176,7 @@
 
   static class CheckOne implements RestReadView<AccountResource.Capability> {
     @Override
-    public Object apply(Capability resource) {
+    public BinaryResult apply(Capability resource) {
       return BinaryResult.create("ok\n");
     }
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetHttpPassword.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetHttpPassword.java
index 8eaf4b3..7fc82f9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetHttpPassword.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetHttpPassword.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.CurrentUser;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
@@ -33,7 +32,7 @@
 
   @Override
   public String apply(AccountResource rsrc) throws AuthException,
-      ResourceNotFoundException, OrmException {
+      ResourceNotFoundException {
     if (self.get() != rsrc.getUser()
         && !self.get().getCapabilities().canAdministrateServer()) {
       throw new AuthException("not allowed to get http password");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
index a860fda..f1b5151 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
@@ -43,7 +43,7 @@
   }
 
   @Override
-  public Object apply(AccountResource rsrc, Input input)
+  public Response<String> apply(AccountResource rsrc, Input input)
       throws ResourceNotFoundException, OrmException {
     Account a = dbProvider.get().accounts().get(rsrc.getUser().getAccountId());
     if (a == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutEmail.java
index b79d8dc4..ba12bbf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutEmail.java
@@ -15,12 +15,13 @@
 package com.google.gerrit.server.account;
 
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.account.CreateEmail.Input;
 
 public class PutEmail implements RestModifyView<AccountResource.Email, Input> {
   @Override
-  public Object apply(AccountResource.Email rsrc, Input input)
+  public Response<?> apply(AccountResource.Email rsrc, Input input)
       throws ResourceConflictException {
     throw new ResourceConflictException("email exists");
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java
index 0e335d0..b8984ab 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java
@@ -42,7 +42,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.UnsupportedEncodingException;
 import java.util.Collections;
 
 class StarredChanges implements
@@ -65,7 +64,7 @@
 
   @Override
   public AccountResource.StarredChange parse(AccountResource parent, IdString id)
-      throws ResourceNotFoundException, OrmException, UnsupportedEncodingException {
+      throws ResourceNotFoundException, OrmException {
     IdentifiedUser user = parent.getUser();
     try {
       user.asyncStarredChanges();
@@ -107,9 +106,6 @@
           .setChange(changes.parse(TopLevelResource.INSTANCE, id));
     } catch (ResourceNotFoundException e) {
       throw new UnprocessableEntityException(String.format("change %s not found", id.get()));
-    } catch (UnsupportedEncodingException e) {
-      log.error("cannot resolve change", e);
-      throw new UnprocessableEntityException("internal server error");
     } catch (OrmException e) {
       log.error("cannot resolve change", e);
       throw new UnprocessableEntityException("internal server error");
@@ -161,7 +157,7 @@
 
     @Override
     public Response<?> apply(AccountResource.StarredChange rsrc, EmptyInput in)
-        throws AuthException, OrmException {
+        throws AuthException {
       if (self.get() != rsrc.getUser()) {
         throw new AuthException("not allowed update starred changes");
       }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/GerritApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/GerritApiImpl.java
new file mode 100644
index 0000000..35700e6
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/GerritApiImpl.java
@@ -0,0 +1,34 @@
+// 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.api;
+
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.changes.Changes;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+class GerritApiImpl implements GerritApi {
+  private final Provider<Changes> changes;
+
+  @Inject
+  GerritApiImpl(Provider<Changes> changes) {
+    this.changes = changes;
+  }
+
+  @Override
+  public Changes changes() {
+    return changes.get();
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/Module.java
similarity index 64%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-server/src/main/java/com/google/gerrit/server/api/Module.java
index a5371d2..6f82a81 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/Module.java
@@ -12,19 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.server.api;
 
-import com.google.common.collect.Maps;
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.inject.AbstractModule;
 
-import java.util.Map;
+public class Module extends AbstractModule {
+  @Override
+  protected void configure() {
+    bind(GerritApi.class).to(GerritApiImpl.class);
 
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
+    install(new com.google.gerrit.server.api.changes.Module());
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
new file mode 100644
index 0000000..855b0c1
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -0,0 +1,142 @@
+// 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.api.changes;
+
+import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.extensions.api.changes.AbandonInput;
+import com.google.gerrit.extensions.api.changes.ChangeApi;
+import com.google.gerrit.extensions.api.changes.Changes;
+import com.google.gerrit.extensions.api.changes.RestoreInput;
+import com.google.gerrit.extensions.api.changes.RevertInput;
+import com.google.gerrit.extensions.api.changes.RevisionApi;
+import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.server.change.Abandon;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.Restore;
+import com.google.gerrit.server.change.Revert;
+import com.google.gerrit.server.change.Revisions;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.assistedinject.Assisted;
+
+import java.io.IOException;
+
+class ChangeApiImpl implements ChangeApi {
+  interface Factory {
+    ChangeApiImpl create(ChangeResource change);
+  }
+
+  private final Changes changeApi;
+  private final Revisions revisions;
+  private final RevisionApiImpl.Factory revisionApi;
+  private final ChangeResource change;
+  private final Provider<Abandon> abandon;
+  private final Provider<Revert> revert;
+  private final Provider<Restore> restore;
+
+  @Inject
+  ChangeApiImpl(Changes changeApi,
+      Revisions revisions,
+      RevisionApiImpl.Factory revisionApi,
+      Provider<Abandon> abandon,
+      Provider<Revert> revert,
+      Provider<Restore> restore,
+      @Assisted ChangeResource change) {
+    this.changeApi = changeApi;
+    this.revert = revert;
+    this.revisions = revisions;
+    this.revisionApi = revisionApi;
+    this.abandon = abandon;
+    this.restore = restore;
+    this.change = change;
+  }
+
+  @Override
+  public String id() {
+    return Integer.toString(change.getChange().getId().get());
+  }
+
+  @Override
+  public RevisionApi current() throws RestApiException {
+    return revision("current");
+  }
+
+  @Override
+  public RevisionApi revision(int id) throws RestApiException {
+    return revision(String.valueOf(id));
+  }
+
+  @Override
+  public RevisionApi revision(String id) throws RestApiException {
+    try {
+      return revisionApi.create(
+          revisions.parse(change, IdString.fromDecoded(id)));
+    } catch (OrmException e) {
+      throw new RestApiException("Cannot parse revision", e);
+    }
+  }
+
+  @Override
+  public void abandon() throws RestApiException {
+    abandon(new AbandonInput());
+  }
+
+  @Override
+  public void abandon(AbandonInput in) throws RestApiException {
+    try {
+      abandon.get().apply(change, in);
+    } catch (OrmException e) {
+      throw new RestApiException("Cannot abandon change", e);
+    } catch (IOException e) {
+      throw new RestApiException("Cannot abandon change", e);
+    }
+  }
+
+  @Override
+  public void restore() throws RestApiException {
+    restore(new RestoreInput());
+  }
+
+  @Override
+  public void restore(RestoreInput in) throws RestApiException {
+    try {
+      restore.get().apply(change, in);
+    } catch (OrmException e) {
+      throw new RestApiException("Cannot restore change", e);
+    } catch (IOException e) {
+      throw new RestApiException("Cannot restore change", e);
+    }
+  }
+
+  @Override
+  public ChangeApi revert() throws RestApiException {
+    return revert(new RevertInput());
+  }
+
+  @Override
+  public ChangeApi revert(RevertInput in) throws RestApiException {
+    try {
+      return changeApi.id(revert.get().apply(change, in)._number);
+    } catch (OrmException e) {
+      throw new RestApiException("Cannot revert change", e);
+    } catch (EmailException e) {
+      throw new RestApiException("Cannot revert change", e);
+    } catch (IOException e) {
+      throw new RestApiException("Cannot revert change", e);
+    }
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangesImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangesImpl.java
new file mode 100644
index 0000000..fdd0817
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangesImpl.java
@@ -0,0 +1,63 @@
+// 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.api.changes;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.extensions.api.changes.ChangeApi;
+import com.google.gerrit.extensions.api.changes.Changes;
+import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.TopLevelResource;
+import com.google.gerrit.extensions.restapi.Url;
+import com.google.gerrit.server.change.ChangesCollection;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+
+class ChangesImpl implements Changes {
+  private final ChangesCollection changes;
+  private final ChangeApiImpl.Factory api;
+
+  @Inject
+  ChangesImpl(ChangesCollection changes, ChangeApiImpl.Factory api) {
+    this.changes = changes;
+    this.api = api;
+  }
+
+  @Override
+  public ChangeApi id(int id) throws RestApiException {
+    return id(String.valueOf(id));
+  }
+
+  @Override
+  public ChangeApi id(String project, String branch, String id)
+      throws RestApiException {
+    return id(Joiner.on('~').join(ImmutableList.of(
+        Url.encode(project),
+        Url.encode(branch),
+        Url.encode(id))));
+  }
+
+  @Override
+  public ChangeApi id(String id) throws RestApiException {
+    try {
+      return api.create(changes.parse(
+          TopLevelResource.INSTANCE,
+          IdString.fromUrl(id)));
+    } catch (OrmException e) {
+      throw new RestApiException("Cannot parse change", e);
+    }
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/Module.java
similarity index 61%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-server/src/main/java/com/google/gerrit/server/api/changes/Module.java
index a5371d2..dbf5f27 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/Module.java
@@ -12,19 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.server.api.changes;
 
-import com.google.common.collect.Maps;
+import com.google.gerrit.extensions.api.changes.Changes;
+import com.google.gerrit.server.config.FactoryModule;
 
-import java.util.Map;
+public class Module extends FactoryModule {
+  @Override
+  protected void configure() {
+    bind(Changes.class).to(ChangesImpl.class);
 
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
+    factory(ChangeApiImpl.Factory.class);
+    factory(RevisionApiImpl.Factory.class);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
new file mode 100644
index 0000000..4e401c0
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
@@ -0,0 +1,108 @@
+// 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.api.changes;
+
+import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.api.changes.RevisionApi;
+import com.google.gerrit.extensions.api.changes.SubmitInput;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.server.change.DeleteDraftPatchSet;
+import com.google.gerrit.server.change.PostReview;
+import com.google.gerrit.server.change.Rebase;
+import com.google.gerrit.server.change.RevisionResource;
+import com.google.gerrit.server.change.Submit;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.assistedinject.Assisted;
+
+import java.io.IOException;
+
+class RevisionApiImpl implements RevisionApi {
+  interface Factory {
+    RevisionApiImpl create(RevisionResource r);
+  }
+
+  private final Provider<DeleteDraftPatchSet> deleteDraft;
+  private final Provider<Rebase> rebase;
+  private final Provider<PostReview> review;
+  private final Provider<Submit> submit;
+  private final RevisionResource revision;
+
+  @Inject
+  RevisionApiImpl(Provider<DeleteDraftPatchSet> deleteDraft,
+      Provider<Rebase> rebase,
+      Provider<PostReview> review,
+      Provider<Submit> submit,
+      @Assisted RevisionResource r) {
+    this.deleteDraft = deleteDraft;
+    this.rebase = rebase;
+    this.review = review;
+    this.submit = submit;
+    this.revision = r;
+  }
+
+  @Override
+  public void review(ReviewInput in) throws RestApiException {
+    try {
+      review.get().apply(revision, in);
+    } catch (OrmException e) {
+      throw new RestApiException("Cannot post review", e);
+    } catch (IOException e) {
+      throw new RestApiException("Cannot post review", e);
+    }
+  }
+
+  @Override
+  public void submit() throws RestApiException {
+    SubmitInput in = new SubmitInput();
+    in.waitForMerge = true;
+    submit(in);
+  }
+
+  @Override
+  public void submit(SubmitInput in) throws RestApiException {
+    try {
+      submit.get().apply(revision, in);
+    } catch (OrmException e) {
+      throw new RestApiException("Cannot submit change", e);
+    } catch (IOException e) {
+      throw new RestApiException("Cannot submit change", e);
+    }
+  }
+
+  @Override
+  public void delete() throws RestApiException {
+    try {
+      deleteDraft.get().apply(revision, null);
+    } catch (OrmException e) {
+      throw new RestApiException("Cannot delete draft ps", e);
+    } catch (IOException e) {
+      throw new RestApiException("Cannot delete draft ps", e);
+    }
+  }
+
+  @Override
+  public void rebase() throws RestApiException {
+    try {
+      rebase.get().apply(revision, null);
+    } catch (OrmException e) {
+      throw new RestApiException("Cannot rebase ps", e);
+    } catch (EmailException e) {
+      throw new RestApiException("Cannot rebase ps", e);
+    }
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java
index 4959cfb..de0438b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java
@@ -17,9 +17,8 @@
 import com.google.common.base.Strings;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.gerrit.common.ChangeHooks;
+import com.google.gerrit.extensions.api.changes.AbandonInput;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.DefaultInput;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.webui.UiAction;
@@ -29,7 +28,6 @@
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.change.Abandon.Input;
 import com.google.gerrit.server.change.ChangeJson.ChangeInfo;
 import com.google.gerrit.server.index.ChangeIndexer;
 import com.google.gerrit.server.mail.AbandonedSender;
@@ -46,7 +44,7 @@
 import java.io.IOException;
 import java.util.Collections;
 
-public class Abandon implements RestModifyView<ChangeResource, Input>,
+public class Abandon implements RestModifyView<ChangeResource, AbandonInput>,
     UiAction<ChangeResource> {
   private static final Logger log = LoggerFactory.getLogger(Abandon.class);
 
@@ -56,11 +54,6 @@
   private final ChangeJson json;
   private final ChangeIndexer indexer;
 
-  public static class Input {
-    @DefaultInput
-    public String message;
-  }
-
   @Inject
   Abandon(ChangeHooks hooks,
       AbandonedSender.Factory abandonedSenderFactory,
@@ -75,9 +68,9 @@
   }
 
   @Override
-  public Object apply(ChangeResource req, Input input)
-      throws BadRequestException, AuthException,
-      ResourceConflictException, Exception {
+  public ChangeInfo apply(ChangeResource req, AbandonInput input)
+      throws AuthException, ResourceConflictException, OrmException,
+      IOException {
     ChangeControl control = req.getControl();
     IdentifiedUser caller = (IdentifiedUser) control.getCurrentUser();
     Change change = req.getChange();
@@ -144,7 +137,7 @@
           && resource.getControl().canAbandon());
   }
 
-  private ChangeMessage newMessage(Input input, IdentifiedUser caller,
+  private ChangeMessage newMessage(AbandonInput input, IdentifiedUser caller,
       Change change) throws OrmException {
     StringBuilder msg = new StringBuilder();
     msg.append("Abandoned");
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 5a3ab3b..87229b3 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
@@ -561,10 +561,10 @@
           Maps.newHashMapWithExpectedSize(labels.size());
 
       if (detailed) {
-        for (String name : labels.keySet()) {
+        for (Map.Entry<String, LabelInfo> entry : labels.entrySet()) {
           ApprovalInfo ai = approvalInfo(accountId, 0, null);
-          byLabel.put(name, ai);
-          labels.get(name).addApproval(ai);
+          byLabel.put(entry.getKey(), ai);
+          entry.getValue().addApproval(ai);
         }
       }
       for (PatchSetApproval psa : current.get(accountId)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
index e93a0d8..b7d8819 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
@@ -31,7 +31,6 @@
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
-import java.io.UnsupportedEncodingException;
 import java.util.Collections;
 import java.util.List;
 
@@ -69,8 +68,7 @@
 
   @Override
   public ChangeResource parse(TopLevelResource root, IdString id)
-      throws ResourceNotFoundException, OrmException,
-      UnsupportedEncodingException {
+      throws ResourceNotFoundException, OrmException {
     List<Change> changes = findChanges(id.encoded());
     if (changes.size() != 1) {
       throw new ResourceNotFoundException(id);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java
index 18f45bb..ce0d33e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java
@@ -14,22 +14,29 @@
 
 package com.google.gerrit.server.change;
 
+import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.webui.UiAction;
 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.change.ChangeJson.ChangeInfo;
 import com.google.gerrit.server.change.CherryPick.Input;
 import com.google.gerrit.server.git.MergeException;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.InvalidChangeOperationException;
+import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.RefControl;
+import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
+import java.io.IOException;
+
 class CherryPick implements RestModifyView<RevisionResource, Input>,
     UiAction<RevisionResource> {
   private final Provider<ReviewDb> dbProvider;
@@ -51,9 +58,9 @@
   }
 
   @Override
-  public Object apply(RevisionResource revision, Input input)
+  public ChangeInfo apply(RevisionResource revision, Input input)
       throws AuthException, BadRequestException, ResourceConflictException,
-      Exception {
+      ResourceNotFoundException, OrmException, IOException, EmailException {
     final ChangeControl control = revision.getControl();
 
     if (input.message == null || input.message.trim().isEmpty()) {
@@ -89,6 +96,8 @@
       throw new BadRequestException(e.getMessage());
     } catch (MergeException  e) {
       throw new ResourceConflictException(e.getMessage());
+    } catch (NoSuchChangeException e) {
+      throw new ResourceNotFoundException(e.getMessage());
     }
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java
index 229e072..afd0b85 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java
@@ -16,9 +16,7 @@
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.changes.Side;
-import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.Url;
@@ -44,7 +42,7 @@
 
   @Override
   public Response<CommentInfo> apply(RevisionResource rsrc, Input in)
-      throws AuthException, BadRequestException, ResourceConflictException, OrmException {
+      throws BadRequestException, OrmException {
     if (Strings.isNullOrEmpty(in.path)) {
       throw new BadRequestException("path must be non-empty");
     } else if (in.message == null || in.message.trim().isEmpty()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraft.java
index 0d5898e..588c372 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraft.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraft.java
@@ -36,7 +36,8 @@
   }
 
   @Override
-  public Object apply(DraftResource rsrc, Input input) throws OrmException {
+  public Response<CommentInfo> apply(DraftResource rsrc, Input input)
+      throws OrmException {
     db.get().patchComments().delete(Collections.singleton(rsrc.getComment()));
     return Response.none();
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChange.java
index 767d5ee..efdb9cb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChange.java
@@ -58,7 +58,7 @@
   }
 
   @Override
-  public Object apply(ChangeResource rsrc, Input input)
+  public Response<?> apply(ChangeResource rsrc, Input input)
       throws ResourceConflictException, AuthException,
       ResourceNotFoundException, OrmException, IOException {
     if (rsrc.getChange().getStatus() != Status.DRAFT) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
index 3265c81..ce86721 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
@@ -64,9 +64,9 @@
   }
 
   @Override
-  public Object apply(RevisionResource rsrc, Input input)
-      throws ResourceNotFoundException, AuthException, OrmException,
-      IOException, ResourceConflictException {
+  public Response<?> apply(RevisionResource rsrc, Input input)
+      throws AuthException, ResourceNotFoundException,
+      ResourceConflictException, OrmException, IOException {
     PatchSet patchSet = rsrc.getPatchSet();
     PatchSet.Id patchSetId = patchSet.getId();
     Change change = rsrc.getChange();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
index c58fc6c..bc726aa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
@@ -50,7 +50,7 @@
   }
 
   @Override
-  public Object apply(ReviewerResource rsrc, Input input)
+  public Response<?> apply(ReviewerResource rsrc, Input input)
       throws AuthException, ResourceNotFoundException, OrmException,
       IOException {
     ChangeControl control = rsrc.getControl();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/EditMessage.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/EditMessage.java
index a634b7c..8bc7a0e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/EditMessage.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/EditMessage.java
@@ -78,8 +78,8 @@
 
   @Override
   public ChangeInfo apply(RevisionResource rsrc, Input input)
-      throws BadRequestException, ResourceConflictException, EmailException,
-      OrmException, ResourceNotFoundException, IOException {
+      throws BadRequestException, ResourceConflictException,
+      ResourceNotFoundException, EmailException, OrmException, IOException {
     if (Strings.isNullOrEmpty(input.message)) {
       throw new BadRequestException("message must be non-empty");
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java
index 11a9edd..c8ff2f0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.change;
 
 import com.google.common.collect.Lists;
+import com.google.gerrit.extensions.api.changes.ReviewInput.NotifyHandling;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
@@ -22,7 +23,6 @@
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.change.PostReview.NotifyHandling;
 import com.google.gerrit.server.git.WorkQueue;
 import com.google.gerrit.server.mail.CommentSender;
 import com.google.gerrit.server.patch.PatchSetInfoFactory;
@@ -61,7 +61,7 @@
   private final SchemaFactory<ReviewDb> schemaFactory;
   private final ThreadLocalRequestContext requestContext;
 
-  private final PostReview.NotifyHandling notify;
+  private final NotifyHandling notify;
   private final Change change;
   private final PatchSet patchSet;
   private final Account.Id authorId;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java
index 98e2ee9..b440ee0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java
@@ -117,13 +117,12 @@
     }
 
     @Override
-    public Object apply(RevisionResource resource)
-        throws ResourceNotFoundException, OrmException,
-        PatchListNotAvailableException, BadRequestException, AuthException {
+    public Response<?> apply(RevisionResource resource) throws AuthException,
+        BadRequestException, ResourceNotFoundException, OrmException {
       if (base != null && reviewed) {
         throw new BadRequestException("cannot combine base and reviewed");
       } else if (reviewed) {
-        return reviewed(resource);
+        return Response.ok(reviewed(resource));
       }
 
       PatchSet basePatchSet = null;
@@ -132,17 +131,21 @@
             resource.getChangeResource(), IdString.fromDecoded(base));
         basePatchSet = baseResource.getPatchSet();
       }
-      Response<Map<String, FileInfo>> r = Response.ok(fileInfoJson.toFileInfoMap(
-          resource.getChange(),
-          resource.getPatchSet(),
-          basePatchSet));
-      if (resource.isCacheable()) {
-        r.caching(CacheControl.PRIVATE(7, TimeUnit.DAYS));
+      try {
+        Response<Map<String, FileInfo>> r = Response.ok(fileInfoJson.toFileInfoMap(
+            resource.getChange(),
+            resource.getPatchSet(),
+            basePatchSet));
+        if (resource.isCacheable()) {
+          r.caching(CacheControl.PRIVATE(7, TimeUnit.DAYS));
+        }
+        return r;
+      } catch (PatchListNotAvailableException e) {
+        throw new ResourceNotFoundException(e.getMessage());
       }
-      return r;
     }
 
-    private Object reviewed(RevisionResource resource)
+    private List<String> reviewed(RevisionResource resource)
         throws AuthException, OrmException {
       CurrentUser user = self.get();
       if (!(user.isIdentifiedUser())) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetChange.java
index 7213a94..8b794f5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetChange.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.extensions.restapi.CacheControl;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.ChangeJson.ChangeInfo;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
@@ -28,7 +29,7 @@
 public class GetChange implements RestReadView<ChangeResource> {
   private final ChangeJson json;
 
-  @Option(name = "-o", multiValued = true, usage = "Output options")
+  @Option(name = "-o", usage = "Output options")
   void addOption(ListChangesOption o) {
     json.addOption(o);
   }
@@ -44,15 +45,15 @@
   }
 
   @Override
-  public Object apply(ChangeResource rsrc) throws OrmException {
+  public Response<ChangeInfo> apply(ChangeResource rsrc) throws OrmException {
     return cache(json.format(rsrc));
   }
 
-  Object apply(RevisionResource rsrc) throws OrmException {
+  Response<ChangeInfo> apply(RevisionResource rsrc) throws OrmException {
     return cache(json.format(rsrc));
   }
 
-  private Object cache(Object res) {
+  private Response<ChangeInfo> cache(ChangeInfo res) {
     return Response.ok(res)
         .caching(CacheControl.PRIVATE(0, TimeUnit.SECONDS).setMustRevalidate());
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetComment.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetComment.java
index 68b0435..3606eed 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetComment.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetComment.java
@@ -29,7 +29,7 @@
   }
 
   @Override
-  public Object apply(CommentResource rsrc) throws OrmException {
+  public CommentInfo apply(CommentResource rsrc) throws OrmException {
     AccountInfo.Loader accountLoader = accountLoaderFactory.create(true);
     CommentInfo ci = new CommentInfo(rsrc.getComment(), accountLoader);
     accountLoader.fill();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetCommit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetCommit.java
index 7d29449..b17e406 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetCommit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetCommit.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.change;
 
 import com.google.gerrit.extensions.restapi.CacheControl;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.ChangeJson.CommitInfo;
@@ -34,11 +35,16 @@
 
   @Override
   public Response<CommitInfo> apply(RevisionResource resource)
-      throws OrmException, PatchSetInfoNotAvailableException {
-    Response<CommitInfo> r = Response.ok(json.toCommit(resource.getPatchSet()));
-    if (resource.isCacheable()) {
-      r.caching(CacheControl.PRIVATE(7, TimeUnit.DAYS));
+      throws ResourceNotFoundException, OrmException {
+    try {
+      Response<CommitInfo> r =
+          Response.ok(json.toCommit(resource.getPatchSet()));
+      if (resource.isCacheable()) {
+        r.caching(CacheControl.PRIVATE(7, TimeUnit.DAYS));
+      }
+      return r;
+    } catch (PatchSetInfoNotAvailableException e) {
+      throw new ResourceNotFoundException(e.getMessage());
     }
-    return r;
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDetail.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDetail.java
index 936edd6..520a09f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDetail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDetail.java
@@ -15,7 +15,9 @@
 package com.google.gerrit.server.change;
 
 import com.google.gerrit.common.changes.ListChangesOption;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.ChangeJson.ChangeInfo;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
@@ -24,7 +26,7 @@
 public class GetDetail implements RestReadView<ChangeResource> {
   private final GetChange delegate;
 
-  @Option(name = "-o", multiValued = true, usage = "Output options")
+  @Option(name = "-o", usage = "Output options")
   void addOption(ListChangesOption o) {
     delegate.addOption(o);
   }
@@ -44,7 +46,7 @@
   }
 
   @Override
-  public Object apply(ChangeResource rsrc) throws OrmException {
+  public Response<ChangeInfo> apply(ChangeResource rsrc) throws OrmException {
     return delegate.apply(rsrc);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java
index 753a70b..04136bd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.common.data.PatchScript.FileMode;
 import com.google.gerrit.extensions.restapi.CacheControl;
 import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
@@ -38,6 +39,7 @@
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
+
 import org.eclipse.jgit.diff.Edit;
 import org.eclipse.jgit.diff.ReplaceEdit;
 import org.kohsuke.args4j.CmdLineException;
@@ -76,8 +78,8 @@
   }
 
   @Override
-  public Object apply(FileResource resource)
-      throws OrmException, NoSuchChangeException, LargeObjectException, ResourceNotFoundException {
+  public Response<Result> apply(FileResource resource)
+      throws ResourceConflictException, ResourceNotFoundException, OrmException {
     PatchSet.Id basePatchSet = null;
     if (base != null) {
       RevisionResource baseResource = revisions.get().parse(
@@ -89,74 +91,80 @@
     prefs.setContext(context);
     prefs.setIntralineDifference(intraline);
 
-    PatchScript ps = patchScriptFactoryFactory.create(
-        resource.getRevision().getControl(),
-        resource.getPatchKey().getFileName(),
-        basePatchSet,
-        resource.getPatchKey().getParentKey(),
-        prefs)
-          .call();
+    try {
+      PatchScript ps = patchScriptFactoryFactory.create(
+          resource.getRevision().getControl(),
+          resource.getPatchKey().getFileName(),
+          basePatchSet,
+          resource.getPatchKey().getParentKey(),
+          prefs)
+            .call();
 
-    Content content = new Content(ps);
-    for (Edit edit : ps.getEdits()) {
-      if (edit.getType() == Edit.Type.EMPTY) {
-        continue;
+      Content content = new Content(ps);
+      for (Edit edit : ps.getEdits()) {
+        if (edit.getType() == Edit.Type.EMPTY) {
+          continue;
+        }
+        content.addCommon(edit.getBeginA());
+
+        checkState(content.nextA == edit.getBeginA(),
+            "nextA = %d; want %d", content.nextA, edit.getBeginA());
+        checkState(content.nextB == edit.getBeginB(),
+            "nextB = %d; want %d", content.nextB, edit.getBeginB());
+        switch (edit.getType()) {
+          case DELETE:
+          case INSERT:
+          case REPLACE:
+            List<Edit> internalEdit = edit instanceof ReplaceEdit
+              ? ((ReplaceEdit) edit).getInternalEdits()
+              : null;
+            content.addDiff(edit.getEndA(), edit.getEndB(), internalEdit);
+            break;
+          case EMPTY:
+          default:
+            throw new IllegalStateException();
+        }
       }
-      content.addCommon(edit.getBeginA());
+      content.addCommon(ps.getA().size());
 
-      checkState(content.nextA == edit.getBeginA(),
-          "nextA = %d; want %d", content.nextA, edit.getBeginA());
-      checkState(content.nextB == edit.getBeginB(),
-          "nextB = %d; want %d", content.nextB, edit.getBeginB());
-      switch (edit.getType()) {
-        case DELETE:
-        case INSERT:
-        case REPLACE:
-          List<Edit> internalEdit = edit instanceof ReplaceEdit
-            ? ((ReplaceEdit) edit).getInternalEdits()
-            : null;
-          content.addDiff(edit.getEndA(), edit.getEndB(), internalEdit);
-          break;
-        case EMPTY:
-        default:
-          throw new IllegalStateException();
+      Result result = new Result();
+      if (ps.getDisplayMethodA() != DisplayMethod.NONE) {
+        result.metaA = new FileMeta();
+        result.metaA.name = Objects.firstNonNull(ps.getOldName(), ps.getNewName());
+        result.metaA.setContentType(ps.getFileModeA(), ps.getMimeTypeA());
       }
-    }
-    content.addCommon(ps.getA().size());
 
-    Result result = new Result();
-    if (ps.getDisplayMethodA() != DisplayMethod.NONE) {
-      result.metaA = new FileMeta();
-      result.metaA.name = Objects.firstNonNull(ps.getOldName(), ps.getNewName());
-      result.metaA.setContentType(ps.getFileModeA(), ps.getMimeTypeA());
-    }
-
-    if (ps.getDisplayMethodB() != DisplayMethod.NONE) {
-      result.metaB = new FileMeta();
-      result.metaB.name = ps.getNewName();
-      result.metaB.setContentType(ps.getFileModeB(), ps.getMimeTypeB());
-    }
-
-    if (intraline) {
-      if (ps.hasIntralineTimeout()) {
-        result.intralineStatus = IntraLineStatus.TIMEOUT;
-      } else if (ps.hasIntralineFailure()) {
-        result.intralineStatus = IntraLineStatus.FAILURE;
-      } else {
-        result.intralineStatus = IntraLineStatus.OK;
+      if (ps.getDisplayMethodB() != DisplayMethod.NONE) {
+        result.metaB = new FileMeta();
+        result.metaB.name = ps.getNewName();
+        result.metaB.setContentType(ps.getFileModeB(), ps.getMimeTypeB());
       }
-    }
 
-    result.changeType = ps.getChangeType();
-    if (ps.getPatchHeader().size() > 0) {
-      result.diffHeader = ps.getPatchHeader();
+      if (intraline) {
+        if (ps.hasIntralineTimeout()) {
+          result.intralineStatus = IntraLineStatus.TIMEOUT;
+        } else if (ps.hasIntralineFailure()) {
+          result.intralineStatus = IntraLineStatus.FAILURE;
+        } else {
+          result.intralineStatus = IntraLineStatus.OK;
+        }
+      }
+
+      result.changeType = ps.getChangeType();
+      if (ps.getPatchHeader().size() > 0) {
+        result.diffHeader = ps.getPatchHeader();
+      }
+      result.content = content.lines;
+      Response<Result> r = Response.ok(result);
+      if (resource.isCacheable()) {
+        r.caching(CacheControl.PRIVATE(7, TimeUnit.DAYS));
+      }
+      return r;
+    } catch (NoSuchChangeException e) {
+      throw new ResourceNotFoundException(e.getMessage());
+    } catch (LargeObjectException e) {
+      throw new ResourceConflictException(e.getMessage());
     }
-    result.content = content.lines;
-    Response<Result> r = Response.ok(result);
-    if (resource.isCacheable()) {
-      r.caching(CacheControl.PRIVATE(7, TimeUnit.DAYS));
-    }
-    return r;
   }
 
   static class Result {
@@ -193,7 +201,7 @@
   enum IntraLineStatus {
     OK,
     TIMEOUT,
-    FAILURE;
+    FAILURE
   }
 
   private static class Content {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java
index 6b36048..c8a2d43 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java
@@ -14,15 +14,11 @@
 
 package com.google.gerrit.server.change;
 
-import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 
 class GetDraft implements RestReadView<DraftResource> {
   @Override
-  public Object apply(DraftResource rsrc) throws AuthException,
-      BadRequestException, ResourceConflictException, Exception {
+  public CommentInfo apply(DraftResource rsrc) {
     return new CommentInfo(rsrc.getComment(), null);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java
index 3776b74..325c932 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java
@@ -70,7 +70,7 @@
   }
 
   @Override
-  public Object apply(RevisionResource rsrc)
+  public RelatedInfo apply(RevisionResource rsrc)
       throws RepositoryNotFoundException, IOException, OrmException {
     Repository git = gitMgr.openRepository(rsrc.getChange().getProject());
     try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetReview.java
index 3676a1e..3440dca 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetReview.java
@@ -15,7 +15,9 @@
 package com.google.gerrit.server.change;
 
 import com.google.gerrit.common.changes.ListChangesOption;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.ChangeJson.ChangeInfo;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
@@ -30,7 +32,7 @@
   }
 
   @Override
-  public Object apply(RevisionResource rsrc) throws OrmException {
+  public Response<ChangeInfo> apply(RevisionResource rsrc) throws OrmException {
     return delegate.apply(rsrc);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetReviewer.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetReviewer.java
index 8c41be8..fac4618 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetReviewer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetReviewer.java
@@ -15,9 +15,12 @@
 package com.google.gerrit.server.change;
 
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.ReviewerJson.ReviewerInfo;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
+import java.util.List;
+
 public class GetReviewer implements RestReadView<ReviewerResource> {
   private final ReviewerJson json;
 
@@ -27,7 +30,7 @@
   }
 
   @Override
-  public Object apply(ReviewerResource rsrc) throws OrmException {
+  public List<ReviewerInfo> apply(ReviewerResource rsrc) throws OrmException {
     return json.format(rsrc);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetTopic.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetTopic.java
index 96a5c76..53f71fd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetTopic.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetTopic.java
@@ -16,11 +16,10 @@
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gwtorm.server.OrmException;
 
 class GetTopic implements RestReadView<ChangeResource> {
   @Override
-  public Object apply(ChangeResource rsrc) throws OrmException {
+  public String apply(ChangeResource rsrc) {
     return Strings.nullToEmpty(rsrc.getChange().getTopic());
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedIn.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedIn.java
index 26e7846..8df6957 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedIn.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedIn.java
@@ -47,8 +47,8 @@
   }
 
   @Override
-  public Object apply(ChangeResource rsrc) throws OrmException, IOException,
-      BadRequestException, ResourceConflictException {
+  public IncludedInInfo apply(ChangeResource rsrc) throws BadRequestException,
+      ResourceConflictException, OrmException, IOException {
     ChangeControl ctl = rsrc.getControl();
     PatchSet ps =
         db.patchSets().get(ctl.getChange().currentPatchSetId());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Index.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Index.java
index 6ecc544..77b8e5f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Index.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Index.java
@@ -37,7 +37,7 @@
   }
 
   @Override
-  public Object apply(ChangeResource rsrc, Input input) throws IOException {
+  public Response<?> apply(ChangeResource rsrc, Input input) throws IOException {
     indexer.index(rsrc.getChange());
     return Response.none();
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java
index 97c7694..cb03724 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java
@@ -19,9 +19,6 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.gerrit.common.changes.Side;
-import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -58,8 +55,8 @@
   }
 
   @Override
-  public Object apply(RevisionResource rsrc) throws AuthException,
-      BadRequestException, ResourceConflictException, Exception {
+  public Map<String, List<CommentInfo>> apply(RevisionResource rsrc)
+      throws OrmException {
     Map<String, List<CommentInfo>> out = Maps.newTreeMap();
     AccountInfo.Loader accountLoader =
         includeAuthorInfo() ? accountLoaderFactory.create(true) : null;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListReviewers.java
index 68cc1b4..7022701 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListReviewers.java
@@ -15,16 +15,17 @@
 package com.google.gerrit.server.change;
 
 import com.google.common.collect.Maps;
-import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.change.ReviewerJson.ReviewerInfo;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
+import java.util.List;
 import java.util.Map;
 
 class ListReviewers implements RestReadView<ChangeResource> {
@@ -42,8 +43,7 @@
   }
 
   @Override
-  public Object apply(ChangeResource rsrc) throws BadRequestException,
-      OrmException {
+  public List<ReviewerInfo> apply(ChangeResource rsrc) throws OrmException {
     Map<Account.Id, ReviewerResource> reviewers = Maps.newLinkedHashMap();
     ReviewDb db = dbProvider.get();
     Change.Id changeId = rsrc.getChange().getId();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
index 595e1c8..852b379 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
@@ -35,7 +35,6 @@
 
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
@@ -80,9 +79,8 @@
   }
 
   @Override
-  public MergeableInfo apply(RevisionResource resource)
-      throws ResourceConflictException, BadRequestException, AuthException,
-      OrmException, RepositoryNotFoundException, IOException {
+  public MergeableInfo apply(RevisionResource resource) throws AuthException,
+      ResourceConflictException, BadRequestException, OrmException, IOException {
     Change change = resource.getChange();
     PatchSet ps = resource.getPatchSet();
     MergeableInfo result = new MergeableInfo();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
index e4f4f1f..f604e0b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -82,11 +82,11 @@
    * validation.
    */
   public static enum ValidatePolicy {
-    GERRIT, RECEIVE_COMMITS, NONE;
+    GERRIT, RECEIVE_COMMITS, NONE
   }
 
   public static enum ChangeKind {
-    REWORK, TRIVIAL_REBASE, NO_CODE_CHANGE;
+    REWORK, TRIVIAL_REBASE, NO_CODE_CHANGE
   }
 
   private final ChangeHooks hooks;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index 367087c..6236dee 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -23,14 +23,17 @@
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
 import com.google.gerrit.common.ChangeHooks;
-import com.google.gerrit.common.changes.Side;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRange;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.api.changes.ReviewInput.Comment;
+import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling;
+import com.google.gerrit.extensions.api.changes.ReviewInput.NotifyHandling;
+import com.google.gerrit.extensions.api.changes.ReviewInput.Side;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.DefaultInput;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.extensions.restapi.Url;
@@ -44,7 +47,6 @@
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountsCollection;
-import com.google.gerrit.server.change.PostReview.Input;
 import com.google.gerrit.server.index.ChangeIndexer;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.util.TimeUtil;
@@ -62,65 +64,9 @@
 import java.util.List;
 import java.util.Map;
 
-public class PostReview implements RestModifyView<RevisionResource, Input> {
+public class PostReview implements RestModifyView<RevisionResource, ReviewInput> {
   private static final Logger log = LoggerFactory.getLogger(PostReview.class);
 
-  public static class Input {
-    @DefaultInput
-    public String message;
-
-    public Map<String, Short> labels;
-    public Map<String, List<Comment>> comments;
-
-    /**
-     * If true require all labels to be within the user's permitted ranges based
-     * on access controls, attempting to use a label not granted to the user
-     * will fail the entire modify operation early. If false the operation will
-     * execute anyway, but the proposed labels given by the user will be
-     * modified to be the "best" value allowed by the access controls, or
-     * ignored if the label does not exist.
-     */
-    public boolean strictLabels = true;
-
-    /**
-     * How to process draft comments already in the database that were not also
-     * described in this input request.
-     */
-    public DraftHandling drafts = DraftHandling.DELETE;
-
-    /** Who to send email notifications to after review is stored. */
-    public NotifyHandling notify = NotifyHandling.ALL;
-
-    /**
-     * Account ID, name, email address or username of another user. The review
-     * will be posted/updated on behalf of this named user instead of the
-     * caller. Caller must have the labelAs-$NAME permission granted for each
-     * label that appears in {@link #labels}. This is in addition to the named
-     * user also needing to have permission to use the labels.
-     * <p>
-     * {@link #strictLabels} impacts how labels is processed for the named user,
-     * not the caller.
-     */
-    public String onBehalfOf;
-  }
-
-  public static enum DraftHandling {
-    DELETE, PUBLISH, KEEP;
-  }
-
-  public static enum NotifyHandling {
-    NONE, OWNER, OWNER_REVIEWERS, ALL;
-  }
-
-  public static class Comment {
-    public String id;
-    public Side side;
-    public int line;
-    public String inReplyTo;
-    public String message;
-    public CommentRange range;
-  }
-
   static class Output {
     Map<String, Short> labels;
   }
@@ -152,9 +98,9 @@
   }
 
   @Override
-  public Object apply(RevisionResource revision, Input input)
-      throws AuthException, BadRequestException, OrmException,
-      UnprocessableEntityException, IOException {
+  public Output apply(RevisionResource revision, ReviewInput input)
+      throws AuthException, BadRequestException, UnprocessableEntityException,
+      OrmException, IOException {
     if (input.onBehalfOf != null) {
       revision = onBehalfOf(revision, input);
     }
@@ -210,7 +156,7 @@
     return output;
   }
 
-  private RevisionResource onBehalfOf(RevisionResource rev, Input in)
+  private RevisionResource onBehalfOf(RevisionResource rev, ReviewInput in)
       throws BadRequestException, AuthException, UnprocessableEntityException,
       OrmException {
     if (in.labels == null || in.labels.isEmpty()) {
@@ -370,7 +316,13 @@
         e.setWrittenOn(timestamp);
         e.setSide(c.side == Side.PARENT ? (short) 0 : (short) 1);
         e.setMessage(c.message);
-        e.setRange(c.range);
+        if (c.range != null) {
+          e.setRange(new CommentRange(
+              c.range.startLine,
+              c.range.startCharacter,
+              c.range.endLine,
+              c.range.endCharacter));
+        }
         (create ? ins : upd).add(e);
       }
     }
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 13deeb0..de6c123 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
@@ -28,7 +28,6 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.DefaultInput;
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Account;
@@ -132,8 +131,8 @@
 
   @Override
   public PostResult apply(ChangeResource rsrc, Input input)
-      throws BadRequestException, ResourceNotFoundException, AuthException,
-      UnprocessableEntityException, OrmException, EmailException, IOException {
+      throws AuthException, BadRequestException, UnprocessableEntityException,
+      OrmException, EmailException, IOException {
     if (input.reviewer == null) {
       throw new BadRequestException("missing reviewer field");
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Publish.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Publish.java
index 8b0fe99..f28f342 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Publish.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Publish.java
@@ -59,9 +59,9 @@
   }
 
   @Override
-  public Object apply(RevisionResource rsrc, Input input) throws IOException,
-      ResourceNotFoundException, ResourceConflictException,
-      OrmException, AuthException {
+  public Response<?> apply(RevisionResource rsrc, Input input)
+      throws AuthException, ResourceNotFoundException,
+      ResourceConflictException, OrmException, IOException {
     if (!rsrc.getPatchSet().isDraft()) {
       throw new ResourceConflictException("Patch set is not a draft");
     }
@@ -148,9 +148,9 @@
     }
 
     @Override
-    public Object apply(ChangeResource rsrc, Input input) throws AuthException,
-        ResourceConflictException, ResourceConflictException, IOException,
-        OrmException, ResourceNotFoundException, AuthException {
+    public Response<?> apply(ChangeResource rsrc, Input input)
+        throws AuthException, ResourceConflictException,
+        ResourceNotFoundException, IOException, OrmException {
       PatchSet ps = dbProvider.get().patchSets()
         .get(rsrc.getChange().currentPatchSetId());
       if (ps == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java
index 803af17..f2285f0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java
@@ -15,15 +15,14 @@
 package com.google.gerrit.server.change;
 
 import com.google.gerrit.common.changes.Side;
-import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.DefaultInput;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.Url;
+import com.google.gerrit.reviewdb.client.CommentRange;
 import com.google.gerrit.reviewdb.client.Patch;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.CommentRange;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.change.PutDraft.Input;
 import com.google.gerrit.server.util.TimeUtil;
@@ -59,8 +58,8 @@
   }
 
   @Override
-  public Object apply(DraftResource rsrc, Input in) throws AuthException,
-      BadRequestException, ResourceConflictException, OrmException {
+  public Response<CommentInfo> apply(DraftResource rsrc, Input in) throws
+      BadRequestException, OrmException {
     PatchLineComment c = rsrc.getComment();
     if (in == null || in.message == null || in.message.trim().isEmpty()) {
       return delete.get().apply(rsrc, null);
@@ -90,7 +89,7 @@
     } else {
       db.get().patchComments().update(Collections.singleton(update(c, in)));
     }
-    return new CommentInfo(c, null);
+    return Response.ok(new CommentInfo(c, null));
   }
 
   private PatchLineComment update(PatchLineComment e, Input in) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java
index acf96e6..84ace98 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java
@@ -18,9 +18,7 @@
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.gerrit.common.ChangeHooks;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.DefaultInput;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.webui.UiAction;
@@ -34,6 +32,7 @@
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.AtomicUpdate;
+import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
@@ -61,9 +60,8 @@
   }
 
   @Override
-  public Object apply(ChangeResource req, Input input)
-      throws BadRequestException, AuthException,
-      ResourceConflictException, Exception {
+  public Response<String> apply(ChangeResource req, Input input)
+      throws AuthException, OrmException, IOException {
     if (input == null) {
       input = new Input();
     }
@@ -123,8 +121,8 @@
       indexFuture.checkedGet();
     }
     return Strings.isNullOrEmpty(newTopicName)
-        ? Response.none()
-        : newTopicName;
+        ? Response.<String>none()
+        : Response.ok(newTopicName);
   }
 
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Restore.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Restore.java
index d31d1f0..58494c4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Restore.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Restore.java
@@ -17,8 +17,8 @@
 import com.google.common.base.Strings;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.gerrit.common.ChangeHooks;
+import com.google.gerrit.extensions.api.changes.RestoreInput;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.DefaultInput;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.webui.UiAction;
@@ -30,7 +30,6 @@
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.change.ChangeJson.ChangeInfo;
-import com.google.gerrit.server.change.Restore.Input;
 import com.google.gerrit.server.index.ChangeIndexer;
 import com.google.gerrit.server.mail.ReplyToChangeSender;
 import com.google.gerrit.server.mail.RestoredSender;
@@ -46,7 +45,7 @@
 import java.io.IOException;
 import java.util.Collections;
 
-public class Restore implements RestModifyView<ChangeResource, Input>,
+public class Restore implements RestModifyView<ChangeResource, RestoreInput>,
     UiAction<ChangeResource> {
   private static final Logger log = LoggerFactory.getLogger(Restore.class);
 
@@ -56,11 +55,6 @@
   private final ChangeJson json;
   private final ChangeIndexer indexer;
 
-  public static class Input {
-    @DefaultInput
-    public String message;
-  }
-
   @Inject
   Restore(ChangeHooks hooks,
       RestoredSender.Factory restoredSenderFactory,
@@ -75,8 +69,9 @@
   }
 
   @Override
-  public Object apply(ChangeResource req, Input input)
-      throws Exception {
+  public ChangeInfo apply(ChangeResource req, RestoreInput input)
+      throws AuthException, ResourceConflictException, OrmException,
+      IOException {
     ChangeControl control = req.getControl();
     IdentifiedUser caller = (IdentifiedUser) control.getCurrentUser();
     Change change = req.getChange();
@@ -143,7 +138,7 @@
           && resource.getControl().canRestore());
   }
 
-  private ChangeMessage newMessage(Input input, IdentifiedUser caller,
+  private ChangeMessage newMessage(RestoreInput input, IdentifiedUser caller,
       Change change) throws OrmException {
     StringBuilder msg = new StringBuilder();
     msg.append("Restored");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Revert.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Revert.java
index 73a6b03..5d2ddde 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Revert.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Revert.java
@@ -16,9 +16,12 @@
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.ChangeHooks;
+import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.extensions.api.changes.RevertInput;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.reviewdb.client.Change;
@@ -27,21 +30,25 @@
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.change.Revert.Input;
+import com.google.gerrit.server.change.ChangeJson.ChangeInfo;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.validators.CommitValidators;
 import com.google.gerrit.server.mail.RevertedSender;
 import com.google.gerrit.server.patch.PatchSetInfoFactory;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.InvalidChangeOperationException;
+import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.ssh.NoSshInfo;
+import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Repository;
 
-public class Revert implements RestModifyView<ChangeResource, Input>,
+import java.io.IOException;
+
+public class Revert implements RestModifyView<ChangeResource, RevertInput>,
     UiAction<ChangeResource> {
   private final ChangeHooks hooks;
   private final RevertedSender.Factory revertedSenderFactory;
@@ -53,10 +60,6 @@
   private final PatchSetInfoFactory patchSetInfoFactory;
   private final ChangeInserter.Factory changeInserterFactory;
 
-  public static class Input {
-    public String message;
-  }
-
   @Inject
   Revert(ChangeHooks hooks,
       RevertedSender.Factory revertedSenderFactory,
@@ -79,7 +82,9 @@
   }
 
   @Override
-  public Object apply(ChangeResource req, Input input) throws Exception {
+  public ChangeInfo apply(ChangeResource req, RevertInput input)
+      throws AuthException, BadRequestException, ResourceConflictException,
+      ResourceNotFoundException, IOException, OrmException, EmailException {
     ChangeControl control = req.getControl();
     Change change = req.getChange();
     if (!control.canAddPatchSet()) {
@@ -104,6 +109,8 @@
       return json.format(revertedChangeId);
     } catch (InvalidChangeOperationException e) {
       throw new BadRequestException(e.getMessage());
+    } catch (NoSuchChangeException e) {
+      throw new ResourceNotFoundException(e.getMessage());
     } finally {
       git.close();
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Reviewed.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Reviewed.java
index 022f178..dd27139 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Reviewed.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Reviewed.java
@@ -38,7 +38,7 @@
     }
 
     @Override
-    public Object apply(FileResource resource, Input input)
+    public Response<String> apply(FileResource resource, Input input)
         throws OrmException {
       ReviewDb db = dbProvider.get();
       AccountPatchReview apr = getExisting(db, resource);
@@ -66,7 +66,7 @@
     }
 
     @Override
-    public Object apply(FileResource resource, Input input)
+    public Response<?> apply(FileResource resource, Input input)
         throws OrmException {
       ReviewDb db = dbProvider.get();
       AccountPatchReview apr = getExisting(db, resource);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
index a507096..3851265 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
@@ -21,6 +21,7 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.extensions.api.changes.SubmitInput;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
@@ -34,7 +35,7 @@
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.ProjectUtil;
-import com.google.gerrit.server.change.Submit.Input;
+import com.google.gerrit.server.change.ChangeJson.ChangeInfo;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.MergeQueue;
 import com.google.gerrit.server.index.ChangeIndexer;
@@ -53,14 +54,14 @@
 import java.util.Collections;
 import java.util.List;
 
-public class Submit implements RestModifyView<RevisionResource, Input>,
+public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
     UiAction<RevisionResource> {
   public static class Input {
     public boolean waitForMerge;
   }
 
   public enum Status {
-    SUBMITTED, MERGED;
+    SUBMITTED, MERGED
   }
 
   public static class Output {
@@ -90,9 +91,9 @@
   }
 
   @Override
-  public Output apply(RevisionResource rsrc, Input input) throws AuthException,
-      ResourceConflictException, RepositoryNotFoundException, IOException,
-      OrmException {
+  public Output apply(RevisionResource rsrc, SubmitInput input)
+      throws AuthException, ResourceConflictException,
+      RepositoryNotFoundException, IOException, OrmException {
     ChangeControl control = rsrc.getControl();
     IdentifiedUser caller = (IdentifiedUser) control.getCurrentUser();
     Change change = rsrc.getChange();
@@ -317,7 +318,7 @@
   }
 
   public static class CurrentRevision implements
-      RestModifyView<ChangeResource, Input> {
+      RestModifyView<ChangeResource, SubmitInput> {
     private final Provider<ReviewDb> dbProvider;
     private final Submit submit;
     private final ChangeJson json;
@@ -332,9 +333,9 @@
     }
 
     @Override
-    public Object apply(ChangeResource rsrc, Input input) throws AuthException,
-        ResourceConflictException, RepositoryNotFoundException, IOException,
-        OrmException {
+    public ChangeInfo apply(ChangeResource rsrc, SubmitInput input)
+        throws AuthException, ResourceConflictException,
+        RepositoryNotFoundException, IOException, OrmException {
       PatchSet ps = dbProvider.get().patchSets()
         .get(rsrc.getChange().currentPatchSetId());
       if (ps == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java
index db18f0d..ab16360 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java
@@ -47,7 +47,7 @@
 
 public class TestSubmitRule implements RestModifyView<RevisionResource, Input> {
   public enum Filters {
-    RUN, SKIP;
+    RUN, SKIP
   }
 
   public static class Input {
@@ -72,8 +72,8 @@
   }
 
   @Override
-  public Object apply(RevisionResource rsrc, Input input) throws OrmException,
-      BadRequestException, AuthException {
+  public List<Record> apply(RevisionResource rsrc, Input input)
+      throws AuthException, BadRequestException, OrmException {
     if (input == null) {
       input = new Input();
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java
index 5a4f9e2..f8c1d80 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java
@@ -28,7 +28,6 @@
 import com.google.gerrit.server.project.RuleEvalException;
 import com.google.gerrit.server.project.SubmitRuleEvaluator;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
 import com.googlecode.prolog_cafe.lang.SymbolTerm;
@@ -54,7 +53,7 @@
 
   @Override
   public SubmitType apply(RevisionResource rsrc, Input input)
-      throws OrmException, BadRequestException, AuthException {
+      throws AuthException, BadRequestException {
     if (input == null) {
       input = new Input();
     }
@@ -120,7 +119,7 @@
 
     @Override
     public SubmitType apply(RevisionResource resource)
-        throws BadRequestException, OrmException, AuthException {
+        throws AuthException, BadRequestException {
       return test.apply(resource, null);
     }
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
index 0b414f2..6d01e7c5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
@@ -197,7 +197,7 @@
   }
 
   /** Whether git-over-http should use Gerrit basic authentication scheme. */
-  public boolean isGitBasichAuth() {
+  public boolean isGitBasicAuth() {
     return gitBasicAuth;
   }
 
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 6a90165..87f5271 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
@@ -67,6 +67,7 @@
 import com.google.gerrit.server.auth.UniversalAuthBackend;
 import com.google.gerrit.server.avatar.AvatarProvider;
 import com.google.gerrit.server.cache.CacheRemovalListener;
+import com.google.gerrit.server.documentation.QueryDocumentationExecutor;
 import com.google.gerrit.server.events.EventFactory;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.ChangeCache;
@@ -110,6 +111,7 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.project.SectionSortCache;
 import com.google.gerrit.server.query.change.ChangeQueryBuilder;
+import com.google.gerrit.server.query.change.ConflictsCacheImpl;
 import com.google.gerrit.server.ssh.SshAddressesModule;
 import com.google.gerrit.server.tools.ToolsCatalog;
 import com.google.gerrit.server.util.IdGenerator;
@@ -135,19 +137,21 @@
   protected void configure() {
     bind(EmailExpander.class).toProvider(EmailExpanderProvider.class).in(
         SINGLETON);
+    bind(QueryDocumentationExecutor.class).in(SINGLETON);
 
     bind(IdGenerator.class);
     bind(RulesCache.class);
     install(authModule);
     install(AccountByEmailCacheImpl.module());
     install(AccountCacheImpl.module());
+    install(ChangeCache.module());
+    install(ConflictsCacheImpl.module());
     install(GroupCacheImpl.module());
     install(GroupIncludeCacheImpl.module());
     install(PatchListCacheImpl.module());
     install(ProjectCacheImpl.module());
     install(SectionSortCache.module());
     install(TagCache.module());
-    install(ChangeCache.module());
 
     install(new AccessControlModule());
     install(new CmdLineParserModule());
@@ -224,6 +228,7 @@
     install(new AuditModule());
     install(new com.google.gerrit.server.access.Module());
     install(new com.google.gerrit.server.account.Module());
+    install(new com.google.gerrit.server.api.Module());
     install(new com.google.gerrit.server.change.Module());
     install(new com.google.gerrit.server.config.Module());
     install(new com.google.gerrit.server.group.Module());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ListCapabilities.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ListCapabilities.java
index c64f786..88616e1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ListCapabilities.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ListCapabilities.java
@@ -19,9 +19,6 @@
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.config.CapabilityDefinition;
 import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -43,9 +40,7 @@
 
   @Override
   public Map<String, CapabilityInfo> apply(ConfigResource resource)
-      throws AuthException, BadRequestException, ResourceConflictException,
-      IllegalArgumentException, SecurityException, IllegalAccessException,
-      NoSuchFieldException {
+      throws IllegalAccessException, NoSuchFieldException {
     Map<String, CapabilityInfo> output = Maps.newTreeMap();
     collectCoreCapabilities(output);
     collectPluginCapabilities(output);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ListTopMenus.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ListTopMenus.java
index 68ae5c1..c8e9d1e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ListTopMenus.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ListTopMenus.java
@@ -31,7 +31,7 @@
   }
 
   @Override
-  public Object apply(ConfigResource resource) {
+  public List<TopMenu.MenuEntry> apply(ConfigResource resource) {
     List<TopMenu.MenuEntry> entries = Lists.newArrayList();
     for (TopMenu extension : extensions) {
       entries.addAll(extension.getEntries());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java
index 294d8a5..8df7073 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java
@@ -14,27 +14,45 @@
 
 package com.google.gerrit.server.config;
 
+import com.google.common.collect.Maps;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.git.ProjectLevelConfig;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
+import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
 
 @Singleton
 public class PluginConfigFactory {
+  private static final Logger log =
+      LoggerFactory.getLogger(PluginConfigFactory.class);
+
+  private final SitePaths site;
   private final Config cfg;
   private final ProjectCache projectCache;
   private final ProjectState.Factory projectStateFactory;
+  private final Map<String, Config> pluginConfigs;
 
   @Inject
-  PluginConfigFactory(@GerritServerConfig Config cfg,
+  PluginConfigFactory(SitePaths site, @GerritServerConfig Config cfg,
       ProjectCache projectCache, ProjectState.Factory projectStateFactory) {
+    this.site = site;
     this.cfg = cfg;
     this.projectCache = projectCache;
     this.projectStateFactory = projectStateFactory;
+    this.pluginConfigs = Maps.newHashMap();
   }
 
   /**
@@ -128,4 +146,96 @@
     return getFromProjectConfig(projectName, pluginName).withInheritance(
         projectStateFactory);
   }
+
+  /**
+   * Returns the configuration for the specified plugin that is stored in the
+   * plugin configuration file 'etc/<plugin-name>.config'.
+   *
+   * The plugin configuration is only loaded once and is then cached.
+   *
+   * @param pluginName the name of the plugin for which the configuration should
+   *        be returned
+   * @return the plugin configuration from the 'etc/<plugin-name>.config' file
+   */
+  public Config getGlobalPluginConfig(String pluginName) {
+    if (pluginConfigs.containsKey(pluginName)) {
+      return pluginConfigs.get(pluginName);
+    }
+
+    File pluginConfigFile = new File(site.etc_dir, pluginName + ".config");
+    FileBasedConfig cfg = new FileBasedConfig(pluginConfigFile, FS.DETECTED);
+    pluginConfigs.put(pluginName, cfg);
+    if (!cfg.getFile().exists()) {
+      log.info("No " + pluginConfigFile.getAbsolutePath() + "; assuming defaults");
+      return cfg;
+    }
+
+    try {
+      cfg.load();
+    } catch (IOException e) {
+      log.warn("Failed to load " + pluginConfigFile.getAbsolutePath(), e);
+    } catch (ConfigInvalidException e) {
+      log.warn("Failed to load " + pluginConfigFile.getAbsolutePath(), e);
+    }
+
+    return cfg;
+  }
+
+  /**
+   * Returns the configuration for the specified plugin that is stored in the
+   * '<plugin-name>.config' file in the 'refs/meta/config' branch of the
+   * specified project.
+   *
+   * @param projectName the name of the project for which the plugin
+   *        configuration should be returned
+   * @param pluginName the name of the plugin for which the configuration should
+   *        be returned
+   * @return the plugin configuration from the '<plugin-name>.config' file of
+   *         the specified project
+   * @throws NoSuchProjectException thrown if the specified project does not
+   *         exist
+   */
+  public Config getProjectPluginConfig(Project.NameKey projectName,
+      String pluginName) throws NoSuchProjectException {
+    return getPluginConfig(projectName, pluginName).get();
+  }
+
+  /**
+   * Returns the configuration for the specified plugin that is stored in the
+   * '<plugin-name>.config' file in the 'refs/meta/config' branch of the
+   * specified project. Parameters which are not set in the
+   * '<plugin-name>.config' of this project are inherited from the parent
+   * project's '<plugin-name>.config' files.
+   *
+   * E.g.: child project: [mySection "mySubsection"] myKey = childValue
+   *
+   * parent project: [mySection "mySubsection"] myKey = parentValue anotherKey =
+   * someValue
+   *
+   * return: [mySection "mySubsection"] myKey = childValue anotherKey =
+   * someValue
+   *
+   * @param projectName the name of the project for which the plugin
+   *        configuration should be returned
+   * @param pluginName the name of the plugin for which the configuration should
+   *        be returned
+   * @return the plugin configuration from the '<plugin-name>.config' file of
+   *         the specified project with inheriting non-set parameters from the
+   *         parent projects
+   * @throws NoSuchProjectException thrown if the specified project does not
+   *         exist
+   */
+  public Config getProjectPluginConfigWithInheritance(Project.NameKey projectName,
+      String pluginName) throws NoSuchProjectException {
+    return getPluginConfig(projectName, pluginName).getWithInheritance();
+  }
+
+  private ProjectLevelConfig getPluginConfig(Project.NameKey projectName,
+      String pluginName) throws NoSuchProjectException {
+    ProjectState projectState = projectCache.get(projectName);
+    if (projectState == null) {
+      throw new NoSuchProjectException(projectName);
+    }
+    return projectState.getConfig(pluginName);
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/data/ChangeAttribute.java b/gerrit-server/src/main/java/com/google/gerrit/server/data/ChangeAttribute.java
index 7339829..5f5fd33 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/data/ChangeAttribute.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/data/ChangeAttribute.java
@@ -43,4 +43,5 @@
     public List<DependencyAttribute> dependsOn;
     public List<DependencyAttribute> neededBy;
     public List<SubmitRecordAttribute> submitRecords;
+    public List<AccountAttribute> allReviewers;
 }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-server/src/main/java/com/google/gerrit/server/documentation/Constants.java
similarity index 63%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-server/src/main/java/com/google/gerrit/server/documentation/Constants.java
index a5371d2..bfa2de2 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/documentation/Constants.java
@@ -12,19 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.server.documentation;
 
-import com.google.common.collect.Maps;
+public class Constants {
 
-import java.util.Map;
+  public static final String DOC_FIELD = "doc";
+  public static final String TITLE_FIELD = "title";
+  public static final String URL_FIELD = "url";
 
-public class ReviewInput {
-  Map<String, Integer> labels;
-
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
-  }
+  private Constants() {}
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/documentation/QueryDocumentationExecutor.java b/gerrit-server/src/main/java/com/google/gerrit/server/documentation/QueryDocumentationExecutor.java
new file mode 100644
index 0000000..576ed9f
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/documentation/QueryDocumentationExecutor.java
@@ -0,0 +1,152 @@
+// 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.documentation;
+
+import com.google.common.collect.Lists;
+import com.google.inject.Inject;
+
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.queryparser.classic.ParseException;
+import org.apache.lucene.queryparser.classic.QueryParser;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IndexOutput;
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.util.Version;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class QueryDocumentationExecutor {
+  private static final Logger log =
+      LoggerFactory.getLogger(QueryDocumentationExecutor.class);
+
+  private static final String INDEX_PATH = "index.zip";
+  private static final Version LUCENE_VERSION = Version.LUCENE_44;
+
+  private IndexSearcher searcher;
+  private QueryParser parser;
+
+  public static class DocResult {
+    public String title;
+    public String url;
+    public String content;
+  }
+
+  @Inject
+  public QueryDocumentationExecutor() {
+    try {
+      Directory dir = readIndexDirectory();
+      if (dir == null) {
+        searcher = null;
+        parser = null;
+        return;
+      }
+      IndexReader reader = DirectoryReader.open(dir);
+      searcher = new IndexSearcher(reader);
+      StandardAnalyzer analyzer = new StandardAnalyzer(LUCENE_VERSION);
+      parser = new QueryParser(LUCENE_VERSION, Constants.DOC_FIELD, analyzer);
+    } catch (IOException e) {
+      log.error("Cannot initialize documentation full text index", e);
+      searcher = null;
+      parser = null;
+    }
+  }
+
+  public List<DocResult> doQuery(String q) throws DocQueryException {
+    if (parser == null || searcher == null) {
+      throw new DocQueryException("Documentation search not available");
+    }
+    try {
+      Query query = parser.parse(q);
+      // TODO(fishywang): Currently as we don't have much documentation, we just use MAX_VALUE here
+      // and skipped paging. Maybe add paging later.
+      TopDocs results = searcher.search(query, Integer.MAX_VALUE);
+      ScoreDoc[] hits = results.scoreDocs;
+      int totalHits = results.totalHits;
+
+      List<DocResult> out = Lists.newArrayListWithCapacity(totalHits);
+      for (int i = 0; i < totalHits; i++) {
+        DocResult result = new DocResult();
+        Document doc = searcher.doc(hits[i].doc);
+        result.url = doc.get(Constants.URL_FIELD);
+        result.title = doc.get(Constants.TITLE_FIELD);
+        out.add(result);
+      }
+      return out;
+    } catch (IOException e) {
+      throw new DocQueryException(e);
+    } catch (ParseException e) {
+      throw new DocQueryException(e);
+    }
+  }
+
+  protected Directory readIndexDirectory() throws IOException {
+    Directory dir = new RAMDirectory();
+    byte[] buffer = new byte[4096];
+    InputStream index =
+        QueryDocumentationExecutor.class.getClassLoader()
+            .getResourceAsStream(INDEX_PATH);
+    if (index == null) {
+      log.warn("No index available");
+      return null;
+    }
+    ZipInputStream zip = new ZipInputStream(index);
+    try {
+      ZipEntry entry;
+      while ((entry = zip.getNextEntry()) != null) {
+        IndexOutput out = dir.createOutput(entry.getName(), null);
+        int count;
+        while ((count = zip.read(buffer)) != -1) {
+          out.writeBytes(buffer, count);
+        }
+        out.close();
+      }
+    } finally {
+      zip.close();
+    }
+    // We must NOT call dir.close() here, as DirectoryReader.open() expects an opened directory.
+    return dir;
+  }
+
+  @SuppressWarnings("serial")
+  public static class DocQueryException extends Exception {
+    DocQueryException() {
+    }
+
+    DocQueryException(String msg) {
+      super(msg);
+    }
+
+    DocQueryException(String msg, Throwable e) {
+      super(msg, e);
+    }
+
+    DocQueryException(Throwable e) {
+      super(e);
+    }
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
index 98e803f..58d16dc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.server.events;
 
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
@@ -69,6 +71,7 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 @Singleton
 public class EventFactory {
@@ -158,6 +161,29 @@
   }
 
   /**
+   * Add allReviewers to an existing ChangeAttribute.
+   *
+   * @param a
+   * @param change
+   */
+  public void addAllReviewers(ChangeAttribute a, Change change)
+      throws OrmException {
+    List<PatchSetApproval> approvals =
+        db.get().patchSetApprovals().byChange(change.getId()).toList();
+    if (!approvals.isEmpty()) {
+      a.allReviewers = Lists.newArrayList();
+      Set<Account.Id> seen = Sets.newHashSet();
+      for (PatchSetApproval psa : approvals) {
+        Account.Id id = psa.getAccountId();
+        if (!seen.contains(id)) {
+          seen.add(id);
+          a.allReviewers.add(asAccountAttribute(id));
+        }
+      }
+    }
+  }
+
+  /**
    * Add submitRecords to an existing ChangeAttribute.
    *
    * @param ca
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
index c51a6ec..43e0156 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -62,17 +61,11 @@
   private static final String UNNAMED =
       "Unnamed repository; edit this file to name it for gitweb.";
 
-  public static class Module extends AbstractModule {
+  public static class Module extends LifecycleModule {
     @Override
     protected void configure() {
       bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
-
-      install(new LifecycleModule() {
-        @Override
-        protected void configure() {
-          listener().to(LocalDiskRepositoryManager.Lifecycle.class);
-        }
-      });
+      listener().to(LocalDiskRepositoryManager.Lifecycle.class);
     }
   }
 
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 aa334f3..40addac 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
@@ -951,7 +951,7 @@
   }
 
   private enum RetryStatus {
-    UNSUBMIT, RETRY_NO_MESSAGE, RETRY_ADD_MESSAGE;
+    UNSUBMIT, RETRY_NO_MESSAGE, RETRY_ADD_MESSAGE
   }
 
   private RetryStatus getRetryStatus(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/NotifyConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/NotifyConfig.java
index 3a0c278..abc53f4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/NotifyConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/NotifyConfig.java
@@ -25,7 +25,7 @@
 
 public class NotifyConfig implements Comparable<NotifyConfig> {
   public static enum Header {
-    TO, CC, BCC;
+    TO, CC, BCC
   }
 
   private String name;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectLevelConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectLevelConfig.java
new file mode 100644
index 0000000..fc95608
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectLevelConfig.java
@@ -0,0 +1,97 @@
+// 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;
+
+import com.google.common.collect.Iterables;
+import com.google.gerrit.server.project.ProjectState;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Set;
+
+/** Configuration file in the projects refs/meta/config branch. */
+public class ProjectLevelConfig extends VersionedMetaData {
+  private final String fileName;
+  private final ProjectState project;
+  private Config cfg;
+
+  public ProjectLevelConfig(String fileName, ProjectState project) {
+    this.fileName = fileName;
+    this.project = project;
+  }
+
+  @Override
+  protected String getRefName() {
+    return GitRepositoryManager.REF_CONFIG;
+  }
+
+  @Override
+  protected void onLoad() throws IOException, ConfigInvalidException {
+    cfg = readConfig(fileName);
+  }
+
+  public Config get() {
+    if (cfg == null) {
+      cfg = new Config();
+    }
+    return cfg;
+  }
+
+  public Config getWithInheritance() {
+    Config cfgWithInheritance = new Config();
+    try {
+      cfgWithInheritance.fromText(get().toText());
+    } catch (ConfigInvalidException e) {
+      // cannot happen
+    }
+    ProjectState parent = Iterables.getFirst(project.parents(), null);
+    if (parent != null) {
+      Config parentCfg = parent.getConfig(fileName).getWithInheritance();
+      for (String section : parentCfg.getSections()) {
+        Set<String> allNames = get().getNames(section);
+        for (String name : parentCfg.getNames(section)) {
+          if (!allNames.contains(name)) {
+            cfgWithInheritance.setStringList(section, null, name,
+                Arrays.asList(parentCfg.getStringList(section, null, name)));
+          }
+        }
+
+        for (String subsection : parentCfg.getSubsections(section)) {
+          allNames = get().getNames(section, subsection);
+          for (String name : parentCfg.getNames(section, subsection)) {
+            if (!allNames.contains(name)) {
+              cfgWithInheritance.setStringList(section, subsection, name,
+                  Arrays.asList(parentCfg.getStringList(section, subsection, name)));
+            }
+          }
+        }
+      }
+    }
+    return cfgWithInheritance;
+  }
+
+  @Override
+  protected void onSave(CommitBuilder commit) throws IOException,
+      ConfigInvalidException {
+    if (commit.getMessage() == null || "".equals(commit.getMessage())) {
+      commit.setMessage("Updated configuration\n");
+    }
+    saveConfig(fileName, cfg);
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/QueueProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/QueueProvider.java
index b539416..c7d925c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/QueueProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/QueueProvider.java
@@ -16,7 +16,7 @@
 
 public interface QueueProvider {
   public static enum QueueType {
-    INTERACTIVE, BATCH;
+    INTERACTIVE, BATCH
   }
 
   public WorkQueue.Executor getQueue(QueueType type);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/RebaseSorter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/RebaseSorter.java
index ac1929b..09c970e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/RebaseSorter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/RebaseSorter.java
@@ -64,7 +64,7 @@
             n.statusCode = CommitMergeStatus.MISSING_DEPENDENCY;
             n.missing = new ArrayList<CodeReviewCommit>();
           }
-          n.missing.add((CodeReviewCommit) c);
+          n.missing.add(c);
         } else {
           contents.add(c);
         }
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 06e88e4..48ad01a 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
@@ -328,7 +328,7 @@
       final ChangeCache changeCache,
       final ChangeInserter.Factory changeInserterFactory,
       final CommitValidators.Factory commitValidatorsFactory,
-      @CanonicalWebUrl @Nullable final String canonicalWebUrl,
+      @CanonicalWebUrl final String canonicalWebUrl,
       @GerritPersonIdent final PersonIdent gerritIdent,
       final TrackingFooters trackingFooters,
       final WorkQueue workQueue,
@@ -404,9 +404,10 @@
         if (allRefs == null) {
           try {
             allRefs = rp.getRepository().getRefDatabase().getRefs(ALL);
+          } catch (ServiceMayNotContinueException e) {
+            throw e;
           } catch (IOException e) {
-            ServiceMayNotContinueException ex =
-                new ServiceMayNotContinueException(e.getMessage());
+            ServiceMayNotContinueException ex = new ServiceMayNotContinueException();
             ex.initCause(e);
             throw ex;
           }
@@ -591,22 +592,42 @@
             return input.created;
           }
         });
-    if (!Iterables.isEmpty(created) && canonicalWebUrl != null) {
-      final String url = canonicalWebUrl;
+    if (!Iterables.isEmpty(created)) {
       addMessage("");
       addMessage("New Changes:");
       for (CreateRequest c : created) {
-        StringBuilder m = new StringBuilder()
-            .append("  ")
-            .append(url)
-            .append(c.change.getChangeId());
-        if (c.change.getStatus() == Change.Status.DRAFT) {
-          m.append(" [DRAFT]");
-        }
-        addMessage(m.toString());
+        addMessage(formatChangeUrl(canonicalWebUrl, c.change));
       }
       addMessage("");
     }
+
+    Iterable<ReplaceRequest> updated =
+        Iterables.filter(replaceByChange.values(),
+            new Predicate<ReplaceRequest>() {
+              @Override
+              public boolean apply(ReplaceRequest input) {
+                return !input.skip;
+              }
+            });
+    if (!Iterables.isEmpty(updated)) {
+      addMessage("");
+      addMessage("Updated Changes:");
+      for (ReplaceRequest u : updated) {
+        addMessage(formatChangeUrl(canonicalWebUrl, u.change));
+      }
+      addMessage("");
+    }
+  }
+
+  private static String formatChangeUrl(String url, Change change) {
+    StringBuilder m = new StringBuilder()
+        .append("  ")
+        .append(url)
+        .append(change.getChangeId());
+    if (change.getStatus() == Change.Status.DRAFT) {
+      m.append(" [DRAFT]");
+    }
+    return m.toString();
   }
 
   private void insertChangesAndPatchSets() {
@@ -960,10 +981,10 @@
     RefControl ctl;
     Set<Account.Id> reviewer = Sets.newLinkedHashSet();
     Set<Account.Id> cc = Sets.newLinkedHashSet();
-    RevCommit baseCommit;
+    List<RevCommit> baseCommit;
 
     @Option(name = "--base", metaVar = "BASE", usage = "merge base of changes")
-    ObjectId base;
+    List<ObjectId> base;
 
     @Option(name = "--topic", metaVar = "NAME", usage = "attach topic to changes")
     String topic;
@@ -1114,20 +1135,24 @@
 
     RevWalk walk = rp.getRevWalk();
     if (magicBranch.base != null) {
-      try {
-        magicBranch.baseCommit = walk.parseCommit(magicBranch.base);
-      } catch (IncorrectObjectTypeException notCommit) {
-        reject(cmd, "base must be a commit");
-        return;
-      } catch (MissingObjectException e) {
-        reject(cmd, "base not found");
-        return;
-      } catch (IOException e) {
-        log.warn(String.format(
-            "Project %s cannot read %s",
-            project.getName(), magicBranch.base.name()), e);
-        reject(cmd, "internal server error");
-        return;
+      magicBranch.baseCommit = Lists.newArrayListWithCapacity(
+          magicBranch.base.size());
+      for (ObjectId id : magicBranch.base) {
+        try {
+          magicBranch.baseCommit.add(walk.parseCommit(id));
+        } catch (IncorrectObjectTypeException notCommit) {
+          reject(cmd, "base must be a commit");
+          return;
+        } catch (MissingObjectException e) {
+          reject(cmd, "base not found");
+          return;
+        } catch (IOException e) {
+          log.warn(String.format(
+              "Project %s cannot read %s",
+              project.getName(), id.name()), e);
+          reject(cmd, "internal server error");
+          return;
+        }
       }
     }
 
@@ -1267,7 +1292,9 @@
       Set<ObjectId> existing = Sets.newHashSet();
       walk.markStart(walk.parseCommit(magicBranch.cmd.getNewId()));
       if (magicBranch.baseCommit != null) {
-        walk.markUninteresting(magicBranch.baseCommit);
+        for (RevCommit c : magicBranch.baseCommit) {
+          walk.markUninteresting(c);
+        }
         assert magicBranch.ctl != null;
         Ref targetRef = allRefs.get(magicBranch.ctl.getRefName());
         if (targetRef != null) {
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 530a388..f34ce8b 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
@@ -68,9 +68,10 @@
     if (oldRefs == null) {
       try {
         oldRefs = rp.getRepository().getRefDatabase().getRefs(ALL);
+      } catch (ServiceMayNotContinueException e) {
+        throw e;
       } catch (IOException e) {
-        ServiceMayNotContinueException ex =
-            new ServiceMayNotContinueException(e.getMessage());
+        ServiceMayNotContinueException ex = new ServiceMayNotContinueException();
         ex.initCause(e);
         throw ex;
       }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java
index 8c65b1a..14aae94 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java
@@ -127,9 +127,10 @@
       RevWalk revWalk) throws ServiceMayNotContinueException {
     try {
       return filter(repository.getRefDatabase().getRefs(RefDatabase.ALL));
+    } catch (ServiceMayNotContinueException e) {
+      throw e;
     } catch (IOException e) {
-      ServiceMayNotContinueException ex =
-          new ServiceMayNotContinueException(e.getMessage());
+      ServiceMayNotContinueException ex = new ServiceMayNotContinueException();
       ex.initCause(e);
       throw ex;
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java
index 64210a0..ab145e6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java
@@ -241,7 +241,7 @@
       // prefer to see tasks sorted in: done before running,
       // running before ready, ready before sleeping.
       //
-      DONE, CANCELLED, RUNNING, READY, SLEEPING, OTHER;
+      DONE, CANCELLED, RUNNING, READY, SLEEPING, OTHER
     }
 
     private final Runnable runnable;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java
index 195ea1c..c417dfc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java
@@ -20,7 +20,6 @@
 import com.google.common.collect.Maps;
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.DefaultInput;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
@@ -87,7 +86,7 @@
 
   @Override
   public List<GroupInfo> apply(GroupResource resource, Input input)
-      throws MethodNotAllowedException, AuthException, BadRequestException,
+      throws MethodNotAllowedException, AuthException,
       UnprocessableEntityException, OrmException {
     AccountGroup group = resource.toAccountGroup();
     if (group == null) {
@@ -149,7 +148,7 @@
 
     @Override
     public GroupInfo apply(GroupResource resource, Input input)
-        throws MethodNotAllowedException, AuthException, BadRequestException,
+        throws AuthException, MethodNotAllowedException,
         UnprocessableEntityException, OrmException {
       AddIncludedGroups.Input in = new AddIncludedGroups.Input();
       in.groups = ImmutableList.of(id);
@@ -173,8 +172,8 @@
     }
 
     @Override
-    public Object apply(IncludedGroupResource resource,
-        PutIncludedGroup.Input input) throws MethodNotAllowedException, OrmException {
+    public GroupInfo apply(IncludedGroupResource resource,
+        PutIncludedGroup.Input input) throws OrmException {
       // Do nothing, the group is already included.
       return get.get().apply(resource);
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
index 5ebf504..8ec6a3f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
@@ -200,7 +200,7 @@
     }
 
     @Override
-    public Object apply(GroupResource resource, PutMember.Input input)
+    public AccountInfo apply(GroupResource resource, PutMember.Input input)
         throws AuthException, MethodNotAllowedException,
         UnprocessableEntityException, OrmException {
       AddMembers.Input in = new AddMembers.Input();
@@ -225,7 +225,7 @@
     }
 
     @Override
-    public Object apply(MemberResource resource, PutMember.Input input)
+    public AccountInfo apply(MemberResource resource, PutMember.Input input)
         throws OrmException {
       // Do nothing, the user is already a member.
       return get.get().apply(resource);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java
index 6914cf2..cf8d9d0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
@@ -79,7 +80,7 @@
   @Override
   public GroupInfo apply(TopLevelResource resource, Input input)
       throws AuthException, BadRequestException, UnprocessableEntityException,
-      NameAlreadyUsedException, OrmException {
+      ResourceConflictException, OrmException {
     if (input == null) {
       input = new Input();
     }
@@ -101,6 +102,8 @@
           null);
     } catch (PermissionDeniedException e) {
       throw new AuthException(e.getMessage());
+    } catch (NameAlreadyUsedException e) {
+      throw new ResourceConflictException(e.getMessage());
     }
     return json.format(GroupDescriptions.forAccountGroup(group));
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteIncludedGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteIncludedGroups.java
index c5d95b0..04dcda3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteIncludedGroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteIncludedGroups.java
@@ -19,7 +19,6 @@
 import com.google.common.collect.Maps;
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
@@ -59,8 +58,8 @@
   }
 
   @Override
-  public Object apply(GroupResource resource, Input input)
-      throws MethodNotAllowedException, AuthException, BadRequestException,
+  public Response<?> apply(GroupResource resource, Input input)
+      throws AuthException, MethodNotAllowedException,
       UnprocessableEntityException, OrmException {
     AccountGroup internalGroup = resource.toAccountGroup();
     if (internalGroup == null) {
@@ -143,8 +142,8 @@
     }
 
     @Override
-    public Object apply(IncludedGroupResource resource, Input input)
-        throws MethodNotAllowedException, AuthException, BadRequestException,
+    public Response<?> apply(IncludedGroupResource resource, Input input)
+        throws AuthException, MethodNotAllowedException,
         UnprocessableEntityException, OrmException {
       AddIncludedGroups.Input in = new AddIncludedGroups.Input();
       in.groups = ImmutableList.of(resource.getMember().get());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
index 186d4b6..fd1b8f4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
@@ -16,7 +16,6 @@
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.gerrit.common.errors.NoSuchGroupException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.Response;
@@ -57,7 +56,7 @@
   }
 
   @Override
-  public Object apply(GroupResource resource, Input input)
+  public Response<?> apply(GroupResource resource, Input input)
       throws AuthException, MethodNotAllowedException,
       UnprocessableEntityException, OrmException {
     AccountGroup internalGroup = resource.toAccountGroup();
@@ -141,9 +140,9 @@
     }
 
     @Override
-    public Object apply(MemberResource resource, Input input)
+    public Response<?> apply(MemberResource resource, Input input)
         throws AuthException, MethodNotAllowedException,
-        UnprocessableEntityException, OrmException, NoSuchGroupException {
+        UnprocessableEntityException, OrmException {
       AddMembers.Input in = new AddMembers.Input();
       in._oneMember = resource.getMember().getAccountId().toString();
       return delete.get().apply(resource, in);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
index 03ec067..76e54b6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
@@ -23,9 +23,6 @@
 import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.errors.NoSuchGroupException;
 import com.google.gerrit.common.groups.ListGroupsOption;
-import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.extensions.restapi.Url;
@@ -96,7 +93,7 @@
   @Option(name = "-m", metaVar = "MATCH", usage = "match group substring")
   private String matchSubstring;
 
-  @Option(name = "-o", multiValued = true, usage = "Output options per group")
+  @Option(name = "-o", usage = "Output options per group")
   public void addOption(ListGroupsOption o) {
     options.add(o);
   }
@@ -132,8 +129,7 @@
   }
 
   @Override
-  public Object apply(TopLevelResource resource) throws AuthException,
-      BadRequestException, ResourceConflictException, Exception {
+  public Object apply(TopLevelResource resource) throws OrmException {
     final Map<String, GroupInfo> output = Maps.newTreeMap();
     for (GroupInfo info : get()) {
       output.put(Objects.firstNonNull(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutDescription.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutDescription.java
index dcc0a76..d17a8b6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutDescription.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutDescription.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.group;
 
 import com.google.common.base.Strings;
-import com.google.gerrit.common.errors.NoSuchGroupException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.DefaultInput;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
@@ -47,8 +46,8 @@
   }
 
   @Override
-  public Object apply(GroupResource resource, Input input)
-      throws MethodNotAllowedException, AuthException, NoSuchGroupException,
+  public Response<String> apply(GroupResource resource, Input input)
+      throws AuthException, MethodNotAllowedException,
       ResourceNotFoundException, OrmException {
     if (input == null) {
       input = new Input(); // Delete would set description to null.
@@ -71,7 +70,7 @@
     groupCache.evict(group);
 
     return Strings.isNullOrEmpty(input.description)
-        ? Response.none()
-        : input.description;
+        ? Response.<String>none()
+        : Response.ok(input.description);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutGroup.java
index 9cbed6c..4d3d6be 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutGroup.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutGroup.java
@@ -15,12 +15,13 @@
 package com.google.gerrit.server.group;
 
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.group.CreateGroup.Input;
 
 public class PutGroup implements RestModifyView<GroupResource, Input> {
   @Override
-  public Object apply(GroupResource resource, Input input)
+  public Response<?> apply(GroupResource resource, Input input)
       throws ResourceConflictException {
     throw new ResourceConflictException("Group already exists");
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
index 2cfc93d..4d285f1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
@@ -37,7 +37,7 @@
  */
 public class IndexModule extends LifecycleModule {
   public enum IndexType {
-    SQL, LUCENE, SOLR;
+    SQL, LUCENE, SOLR
   }
 
   /** Type of secondary index. */
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
index cd6409e..cb92b0f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
@@ -17,13 +17,13 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.Ordering;
 import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.extensions.api.changes.ReviewInput.NotifyHandling;
 import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.CommentRange;
 import com.google.gerrit.reviewdb.client.Patch;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.change.PostReview.NotifyHandling;
 import com.google.gerrit.server.patch.PatchFile;
 import com.google.gerrit.server.patch.PatchList;
 import com.google.gerrit.server.patch.PatchListNotAvailableException;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java
index 89fc6b9..b32e7f4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java
@@ -51,7 +51,7 @@
   }
 
   public static enum Encryption {
-    NONE, SSL, TLS;
+    NONE, SSL, TLS
   }
 
   private final boolean enabled;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java
index e1294e0..b71d04a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java
@@ -311,8 +311,8 @@
         MergeFormatter fmt = new MergeFormatter();
         Map<String, MergeResult<? extends Sequence>> r = m.getMergeResults();
         Map<String, ObjectId> resolved = new HashMap<String, ObjectId>();
-        for (String path : r.keySet()) {
-          MergeResult<? extends Sequence> p = r.get(path);
+        for (Map.Entry<String, MergeResult<? extends Sequence>> entry : r.entrySet()) {
+          MergeResult<? extends Sequence> p = entry.getValue();
           TemporaryBuffer buf = new TemporaryBuffer.LocalFile(10 * 1024 * 1024);
           try {
             fmt.formatMerge(buf, p, "BASE", oursName, theirsName, "UTF-8");
@@ -320,7 +320,7 @@
 
             InputStream in = buf.openInputStream();
             try {
-              resolved.put(path, ins.insert(Constants.OBJ_BLOB, buf.length(), in));
+              resolved.put(entry.getKey(), ins.insert(Constants.OBJ_BLOB, buf.length(), in));
             } finally {
               in.close();
             }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/DisablePlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/DisablePlugin.java
index cae5ce6..d6dfa3c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/DisablePlugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/DisablePlugin.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.plugins.DisablePlugin.Input;
+import com.google.gerrit.server.plugins.ListPlugins.PluginInfo;
 import com.google.inject.Inject;
 
 @RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@@ -34,9 +35,9 @@
   }
 
   @Override
-  public Object apply(PluginResource resource, Input input) {
+  public PluginInfo apply(PluginResource resource, Input input) {
     String name = resource.getName();
     loader.disablePlugins(ImmutableSet.of(name));
-    return new ListPlugins.PluginInfo(loader.get(name));
+    return new PluginInfo(loader.get(name));
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/EnablePlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/EnablePlugin.java
index f33d814..ebe6d1a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/EnablePlugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/EnablePlugin.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.plugins.EnablePlugin.Input;
+import com.google.gerrit.server.plugins.ListPlugins.PluginInfo;
 import com.google.inject.Inject;
 
 import java.io.PrintWriter;
@@ -38,7 +39,7 @@
   }
 
   @Override
-  public Object apply(PluginResource resource, Input input)
+  public PluginInfo apply(PluginResource resource, Input input)
       throws ResourceConflictException {
     String name = resource.getName();
     try {
@@ -51,6 +52,6 @@
       pw.flush();
       throw new ResourceConflictException(buf.toString());
     }
-    return new ListPlugins.PluginInfo(loader.get(name));
+    return new PluginInfo(loader.get(name));
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/GetStatus.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/GetStatus.java
index 2207d34..7651506 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/GetStatus.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/GetStatus.java
@@ -15,10 +15,11 @@
 package com.google.gerrit.server.plugins;
 
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.plugins.ListPlugins.PluginInfo;
 
 class GetStatus implements RestReadView<PluginResource> {
   @Override
-  public Object apply(PluginResource resource) {
-    return new ListPlugins.PluginInfo(resource.getPlugin());
+  public PluginInfo apply(PluginResource resource) {
+    return new PluginInfo(resource.getPlugin());
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java
index 6684bfb..f9bdb48 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.server.plugins.InstallPlugin.Input;
+import com.google.gerrit.server.plugins.ListPlugins.PluginInfo;
 import com.google.inject.Inject;
 
 import java.io.IOException;
@@ -52,7 +53,7 @@
   }
 
   @Override
-  public Response<ListPlugins.PluginInfo> apply(TopLevelResource resource,
+  public Response<PluginInfo> apply(TopLevelResource resource,
       Input input) throws BadRequestException, IOException {
     try {
       InputStream in;
@@ -101,7 +102,7 @@
     }
 
     @Override
-    public Response<ListPlugins.PluginInfo> apply(PluginResource resource,
+    public Response<PluginInfo> apply(PluginResource resource,
         Input input) throws BadRequestException, IOException {
       return new InstallPlugin(loader, resource.getName(), false)
         .apply(TopLevelResource.INSTANCE, input);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPlugin.java
index 6adc677..1568997 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPlugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPlugin.java
@@ -17,6 +17,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.annotations.PluginCanonicalWebUrl;
 import com.google.gerrit.extensions.annotations.PluginData;
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.registration.RegistrationHandle;
@@ -59,6 +60,7 @@
   private final JarFile jarFile;
   private final Manifest manifest;
   private final File dataDir;
+  private final String pluginCanonicalWebUrl;
   private final ClassLoader classLoader;
   private Class<? extends Module> sysModule;
   private Class<? extends Module> sshModule;
@@ -71,6 +73,7 @@
   private List<ReloadableRegistrationHandle<?>> reloadableHandles;
 
   public JarPlugin(String name,
+      String pluginCanonicalWebUrl,
       PluginUser pluginUser,
       File srcJar,
       FileSnapshot snapshot,
@@ -83,6 +86,7 @@
       @Nullable Class<? extends Module> sshModule,
       @Nullable Class<? extends Module> httpModule) {
     super(name, srcJar, pluginUser, snapshot, apiType);
+    this.pluginCanonicalWebUrl = pluginCanonicalWebUrl;
     this.jarFile = jarFile;
     this.manifest = manifest;
     this.dataDir = dataDir;
@@ -193,6 +197,9 @@
         bind(String.class)
           .annotatedWith(PluginName.class)
           .toInstance(getName());
+        bind(String.class)
+          .annotatedWith(PluginCanonicalWebUrl.class)
+          .toInstance(pluginCanonicalWebUrl);
 
         bind(File.class)
           .annotatedWith(PluginData.class)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
index ca0f7ae..08c95b2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
@@ -19,9 +19,6 @@
 import com.google.common.collect.Maps;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
-import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.extensions.restapi.Url;
@@ -69,8 +66,8 @@
   }
 
   @Override
-  public Object apply(TopLevelResource resource) throws AuthException,
-      BadRequestException, ResourceConflictException, Exception {
+  public Object apply(TopLevelResource resource)
+      throws UnsupportedEncodingException {
     format = OutputFormat.JSON;
     return display(null);
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java
index d9a2c0f..9bf4085 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java
@@ -34,7 +34,7 @@
 
 public abstract class Plugin {
   public static enum ApiType {
-    EXTENSION, PLUGIN, JS;
+    EXTENSION, PLUGIN, JS
   }
 
   /** Unique key that changes whenever a plugin reloads. */
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
index 7569744..4682d2b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.plugins;
 
+import com.google.common.base.CharMatcher;
 import com.google.common.base.Joiner;
 import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
@@ -30,6 +31,7 @@
 import com.google.gerrit.extensions.systemstatus.ServerInformation;
 import com.google.gerrit.extensions.webui.JavaScriptPlugin;
 import com.google.gerrit.server.PluginUser;
+import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
@@ -86,6 +88,7 @@
   private final Queue<Plugin> toCleanup;
   private final Provider<PluginCleanerTask> cleaner;
   private final PluginScannerThread scanner;
+  private final Provider<String> urlProvider;
 
   @Inject
   public PluginLoader(SitePaths sitePaths,
@@ -93,7 +96,8 @@
       ServerInformationImpl sii,
       PluginUser.Factory puf,
       Provider<PluginCleanerTask> pct,
-      @GerritServerConfig Config cfg) {
+      @GerritServerConfig Config cfg,
+      @CanonicalWebUrl Provider<String> provider) {
     pluginsDir = sitePaths.plugins_dir;
     dataDir = sitePaths.data_dir;
     tmpDir = sitePaths.tmp_dir;
@@ -106,6 +110,7 @@
     toCleanup = Queues.newArrayDeque();
     cleanupHandles = Maps.newConcurrentMap();
     cleaner = pct;
+    urlProvider = provider;
 
     long checkFrequency = ConfigUtil.getTimeUnit(cfg,
         "plugins", null, "checkFrequency",
@@ -344,8 +349,9 @@
     syncDisabledPlugins(jars);
 
     Map<String, File> activePlugins = filterDisabled(jars);
-    for (String name : activePlugins.keySet()) {
-      File jar = activePlugins.get(name);
+    for (Map.Entry<String, File> entry : activePlugins.entrySet()) {
+      String name = entry.getKey();
+      File jar = entry.getValue();
       FileSnapshot brokenTime = broken.get(name);
       if (brokenTime != null && !brokenTime.isModified(jar)) {
         continue;
@@ -504,7 +510,13 @@
       Class<? extends Module> sysModule = load(sysName, pluginLoader);
       Class<? extends Module> sshModule = load(sshName, pluginLoader);
       Class<? extends Module> httpModule = load(httpName, pluginLoader);
-      Plugin plugin = new JarPlugin(name, pluginUserFactory.create(name),
+
+      String url = String.format("%s/plugins/%s/",
+          CharMatcher.is('/').trimTrailingFrom(urlProvider.get()),
+          name);
+
+      Plugin plugin = new JarPlugin(name, url,
+          pluginUserFactory.create(name),
           srcJar, snapshot,
           jarFile, manifest,
           new File(dataDir, name), type, pluginLoader,
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 bd1c3c8..055baf1 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
@@ -16,9 +16,8 @@
 
 import com.google.gerrit.extensions.systemstatus.ServerInformation;
 import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.inject.AbstractModule;
 
-public class PluginModule extends AbstractModule {
+public class PluginModule extends LifecycleModule {
   @Override
   protected void configure() {
     bind(ServerInformationImpl.class);
@@ -28,11 +27,6 @@
     bind(PluginGuiceEnvironment.class);
     bind(PluginLoader.class);
     bind(CopyConfigModule.class);
-    install(new LifecycleModule() {
-      @Override
-      protected void configure() {
-        listener().to(PluginLoader.class);
-      }
-    });
+    listener().to(PluginLoader.class);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ReloadPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ReloadPlugin.java
index 9f9ef2db..32e8b24 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ReloadPlugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ReloadPlugin.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.plugins.ListPlugins.PluginInfo;
 import com.google.gerrit.server.plugins.ReloadPlugin.Input;
 import com.google.inject.Inject;
 
@@ -38,7 +39,7 @@
   }
 
   @Override
-  public Object apply(PluginResource resource, Input input) throws ResourceConflictException {
+  public PluginInfo apply(PluginResource resource, Input input) throws ResourceConflictException {
     String name = resource.getName();
     try {
       loader.reload(ImmutableList.of(name));
@@ -52,6 +53,6 @@
       pw.flush();
       throw new ResourceConflictException(buf.toString());
     }
-    return new ListPlugins.PluginInfo(loader.get(name));
+    return new PluginInfo(loader.get(name));
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index 698a0ac..5c40d16 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -166,6 +166,19 @@
     }
   }
 
+  /**
+   * Exception thrown when the label term of a submit record
+   * unexpectedly didn't contain a user term.
+   */
+  private static class UserTermExpected extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public UserTermExpected(SubmitRecord.Label label) {
+      super(String.format("A label with the status %s must contain a user.",
+          label.toString()));
+    }
+  }
+
   private final RefControl refControl;
   private final Change change;
 
@@ -504,25 +517,29 @@
         lbl.label = state.arg(0).name();
         Term status = state.arg(1);
 
-        if ("ok".equals(status.name())) {
-          lbl.status = SubmitRecord.Label.Status.OK;
-          appliedBy(lbl, status);
+        try {
+          if ("ok".equals(status.name())) {
+            lbl.status = SubmitRecord.Label.Status.OK;
+            appliedBy(lbl, status);
 
-        } else if ("reject".equals(status.name())) {
-          lbl.status = SubmitRecord.Label.Status.REJECT;
-          appliedBy(lbl, status);
+          } else if ("reject".equals(status.name())) {
+            lbl.status = SubmitRecord.Label.Status.REJECT;
+            appliedBy(lbl, status);
 
-        } else if ("need".equals(status.name())) {
-          lbl.status = SubmitRecord.Label.Status.NEED;
+          } else if ("need".equals(status.name())) {
+            lbl.status = SubmitRecord.Label.Status.NEED;
 
-        } else if ("may".equals(status.name())) {
-          lbl.status = SubmitRecord.Label.Status.MAY;
+          } else if ("may".equals(status.name())) {
+            lbl.status = SubmitRecord.Label.Status.MAY;
 
-        } else if ("impossible".equals(status.name())) {
-          lbl.status = SubmitRecord.Label.Status.IMPOSSIBLE;
+          } else if ("impossible".equals(status.name())) {
+            lbl.status = SubmitRecord.Label.Status.IMPOSSIBLE;
 
-        } else {
-          return logInvalidResult(submitRule, submitRecord);
+          } else {
+            return logInvalidResult(submitRule, submitRecord);
+          }
+        } catch (UserTermExpected e) {
+          return logInvalidResult(submitRule, submitRecord, e.getMessage());
         }
       }
 
@@ -593,9 +610,14 @@
     }
   }
 
-  private List<SubmitRecord> logInvalidResult(Term rule, Term record) {
+  private List<SubmitRecord> logInvalidResult(Term rule, Term record, String reason) {
     return logRuleError("Submit rule " + rule + " for change " + change.getId()
-        + " of " + getProject().getName() + " output invalid result: " + record);
+        + " of " + getProject().getName() + " output invalid result: " + record
+        + (reason == null ? "" : ". Reason: " + reason));
+  }
+
+  private List<SubmitRecord> logInvalidResult(Term rule, Term record) {
+    return logInvalidResult(rule, record, null);
   }
 
   private List<SubmitRecord> logRuleError(String err, Exception e) {
@@ -638,11 +660,14 @@
     return rec;
   }
 
-  private void appliedBy(SubmitRecord.Label label, Term status) {
+  private void appliedBy(SubmitRecord.Label label, Term status)
+      throws UserTermExpected {
     if (status.isStructure() && status.arity() == 1) {
       Term who = status.arg(0);
       if (isUser(who)) {
         label.appliedBy = new Account.Id(((IntegerTerm) who.arg(0)).intValue());
+      } else {
+        throw new UserTermExpected(label);
       }
     }
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
index 0d4f336..58f6b15 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
@@ -32,6 +32,7 @@
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.group.GroupsCollection;
 import com.google.gerrit.server.project.CreateProject.Input;
+import com.google.gerrit.server.project.ProjectJson.ProjectInfo;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
@@ -82,7 +83,7 @@
   }
 
   @Override
-  public Object apply(TopLevelResource resource, Input input)
+  public Response<ProjectInfo> apply(TopLevelResource resource, Input input)
       throws BadRequestException, UnprocessableEntityException,
       ProjectCreationFailedException, IOException {
     if (input == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java
index a41c197..600f65d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java
@@ -59,7 +59,7 @@
   }
 
   @Override
-  public Object apply(BranchResource rsrc, Input input) throws AuthException,
+  public Response<?> apply(BranchResource rsrc, Input input) throws AuthException,
       ResourceConflictException, OrmException, IOException {
     if (!rsrc.getControl().controlForRef(rsrc.getBranchKey()).canDelete()) {
       throw new AuthException("Cannot delete branch");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteDashboard.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteDashboard.java
index da1e46b..669f024 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteDashboard.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteDashboard.java
@@ -18,11 +18,16 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.project.DashboardsCollection.DashboardInfo;
 import com.google.gerrit.server.project.DeleteDashboard.Input;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
+import java.io.IOException;
+
 class DeleteDashboard implements RestModifyView<DashboardResource, Input> {
   static class Input {
     String commitMessage;
@@ -36,9 +41,9 @@
   }
 
   @Override
-  public Object apply(DashboardResource resource, Input input)
+  public Response<DashboardInfo> apply(DashboardResource resource, Input input)
       throws AuthException, BadRequestException, ResourceConflictException,
-      Exception {
+      ResourceNotFoundException, MethodNotAllowedException, IOException {
     if (resource.isProjectDefault()) {
       SetDashboard.Input in = new SetDashboard.Input();
       in.commitMessage = input != null ? input.commitMessage : null;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GarbageCollect.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GarbageCollect.java
index ca7f6f0..b98cb8b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GarbageCollect.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GarbageCollect.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.server.git.GarbageCollection;
 import com.google.gerrit.server.project.GarbageCollect.Input;
 import com.google.inject.Inject;
@@ -31,8 +32,10 @@
 import java.util.Collections;
 
 @RequiresCapability(GlobalCapability.RUN_GC)
-public class GarbageCollect implements RestModifyView<ProjectResource, Input> {
+public class GarbageCollect implements RestModifyView<ProjectResource, Input>,
+    UiAction<ProjectResource> {
   public static class Input {
+    public boolean showProgress;
   }
 
   private GarbageCollection.Factory garbageCollectionFactory;
@@ -43,7 +46,7 @@
   }
 
   @Override
-  public BinaryResult apply(final ProjectResource rsrc, Input input) {
+  public BinaryResult apply(final ProjectResource rsrc, final Input input) {
     return new BinaryResult() {
       @Override
       public void writeTo(OutputStream out) throws IOException {
@@ -56,10 +59,10 @@
         };
         try {
           GarbageCollectionResult result = garbageCollectionFactory.create().run(
-              Collections.singletonList(rsrc.getNameKey()), writer);
+              Collections.singletonList(rsrc.getNameKey()), input.showProgress ? writer : null);
+          String msg = "garbage collection was successfully done";
           if (result.hasErrors()) {
             for (GarbageCollectionResult.Error e : result.getErrors()) {
-              String msg;
               switch (e.getType()) {
                 case REPOSITORY_NOT_FOUND:
                   msg = "error: project \"" + e.getProjectName() + "\" not found";
@@ -76,9 +79,9 @@
                   msg = "error: garbage collection for project \"" + e.getProjectName()
                       + "\" failed: " + e.getType();
               }
-              writer.println(msg);
             }
           }
+          writer.println(msg);
         } finally {
           writer.flush();
         }
@@ -87,4 +90,11 @@
      .setCharacterEncoding(Charsets.UTF_8.name())
      .disableGzip();
   }
+
+  @Override
+  public UiAction.Description getDescription(ProjectResource rsrc) {
+    return new UiAction.Description()
+        .setLabel("Run GC")
+        .setTitle("Triggers the Git Garbage Collection for this project.");
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDashboard.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDashboard.java
index 51d6191..fcd2284 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDashboard.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDashboard.java
@@ -20,6 +20,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.Url;
@@ -45,7 +46,7 @@
 
   @Override
   public DashboardInfo apply(DashboardResource resource)
-      throws ResourceNotFoundException, IOException, ConfigInvalidException {
+      throws ResourceNotFoundException, ResourceConflictException, IOException {
     if (inherited && !resource.isProjectDefault()) {
       // inherited flag can only be used with default.
       throw new ResourceNotFoundException("inherited");
@@ -54,7 +55,11 @@
     String project = resource.getControl().getProject().getName();
     if (resource.isProjectDefault()) {
       // The default is not resolved to a definition yet.
-      resource = defaultOf(resource.getControl());
+      try {
+        resource = defaultOf(resource.getControl());
+      } catch (ConfigInvalidException e) {
+        throw new ResourceConflictException(e.getMessage());
+      }
     }
 
     return DashboardsCollection.parse(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDescription.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDescription.java
index d8fabab..aeff72f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDescription.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDescription.java
@@ -20,7 +20,7 @@
 
 class GetDescription implements RestReadView<ProjectResource> {
   @Override
-  public Object apply(ProjectResource resource) {
+  public String apply(ProjectResource resource) {
     Project project = resource.getControl().getProject();
     return Strings.nullToEmpty(project.getDescription());
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetParent.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetParent.java
index 1cebd87..f790b7e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetParent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetParent.java
@@ -28,7 +28,7 @@
   }
 
   @Override
-  public Object apply(ProjectResource resource) {
+  public String apply(ProjectResource resource) {
     Project project = resource.getControl().getProject();
     Project.NameKey parentName = project.getParent(allProjectsName);
     return parentName != null ? parentName.get() : "";
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetProject.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetProject.java
index a482278..961f7b2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetProject.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetProject.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.project;
 
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.project.ProjectJson.ProjectInfo;
 import com.google.inject.Inject;
 
 class GetProject implements RestReadView<ProjectResource> {
@@ -27,7 +28,7 @@
   }
 
   @Override
-  public Object apply(ProjectResource rsrc) {
+  public ProjectInfo apply(ProjectResource rsrc) {
     return json.format(rsrc);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListChildProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListChildProjects.java
index d4b84dd..58abe40 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListChildProjects.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListChildProjects.java
@@ -47,6 +47,10 @@
     this.projectNodeFactory = projectNodeFactory;
   }
 
+  public void setRecursive(boolean recursive) {
+    this.recursive = recursive;
+  }
+
   @Override
   public List<ProjectInfo> apply(ProjectResource rsrc) {
     if (recursive) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListDashboards.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListDashboards.java
index c063618..e3247fc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListDashboards.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListDashboards.java
@@ -53,7 +53,7 @@
   }
 
   @Override
-  public Object apply(ProjectResource resource)
+  public List<?> apply(ProjectResource resource)
       throws ResourceNotFoundException, IOException {
     ProjectControl ctl = resource.getControl();
     String project = ctl.getProject().getName();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
index 59b544a..e79912e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
@@ -109,7 +109,7 @@
   @Option(name = "--format", usage = "(deprecated) output format")
   private OutputFormat format = OutputFormat.TEXT;
 
-  @Option(name = "--show-branch", aliases = {"-b"}, multiValued = true,
+  @Option(name = "--show-branch", aliases = {"-b"},
       usage = "displays the sha of each project in the specified branch")
   public void addShowBranch(String branch) {
     showBranch.add(branch);
@@ -271,6 +271,7 @@
               info.name = parentState.getProject().getName();
               info.description = Strings.emptyToNull(
                   parentState.getProject().getDescription());
+              info.state = parentState.getProject().getState();
             } else {
               rejected.add(parentState.getProject().getName());
               continue;
@@ -313,6 +314,8 @@
             info.description = Strings.emptyToNull(e.getProject().getDescription());
           }
 
+          info.state = e.getProject().getState();
+
           try {
             if (!showBranch.isEmpty()) {
               Repository git = repoManager.openRepository(projectName);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
index c111c02..8ab3311 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
@@ -506,8 +506,8 @@
           } catch (IncorrectObjectTypeException e) {
             continue;
           }
-          if (rw.isMergedInto(commit, tip)
-              && controlForRef(entry.getKey()).canPerform(Permission.READ)) {
+          if (controlForRef(entry.getKey()).canPerform(Permission.READ)
+              && rw.isMergedInto(commit, tip)) {
             return true;
           }
         }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectJson.java
index 8db5cbb..72910a3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectJson.java
@@ -41,6 +41,7 @@
     Project.NameKey parentName = p.getParent(allProjects);
     info.parent = parentName != null ? parentName.get() : null;
     info.description = Strings.emptyToNull(p.getDescription());
+    info.state = p.getState();
     info.finish();
     return info;
   }
@@ -51,6 +52,7 @@
     public String name;
     public String parent;
     public String description;
+    public Project.State state;
     public Map<String, String> branches;
 
     void finish() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectResource.java
index f4449f0..459e392 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectResource.java
@@ -41,6 +41,10 @@
     return control.getProject().getNameKey();
   }
 
+  public Project.State getState() {
+    return control.getProject().getState();
+  }
+
   public ProjectControl getControl() {
     return control;
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
index 800fa6e..2e79e2f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
@@ -40,12 +40,14 @@
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.git.ProjectLevelConfig;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
 import com.googlecode.prolog_cafe.compiler.CompileException;
 import com.googlecode.prolog_cafe.lang.PrologMachineCopy;
 
+import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.slf4j.Logger;
@@ -83,6 +85,7 @@
   private final List<CommentLinkInfo> commentLinks;
 
   private final ProjectConfig config;
+  private final Map<String, ProjectLevelConfig> configs;
   private final Set<AccountGroup.UUID> localOwners;
 
   /** Prolog rule state. */
@@ -121,6 +124,7 @@
     this.rulesCache = rulesCache;
     this.commentLinks = commentLinks;
     this.config = config;
+    this.configs = Maps.newHashMap();
     this.capabilities = isAllProjects
       ? new CapabilityCollection(config.getAccessSection(AccessSection.GLOBAL_CAPABILITIES))
       : null;
@@ -216,6 +220,29 @@
     return config;
   }
 
+  public ProjectLevelConfig getConfig(String fileName) {
+    if (configs.containsKey(fileName)) {
+      return configs.get(fileName);
+    }
+
+    ProjectLevelConfig cfg = new ProjectLevelConfig(fileName, this);
+    try {
+      Repository git = gitMgr.openRepository(getProject().getNameKey());
+      try {
+        cfg.load(git);
+      } finally {
+        git.close();
+      }
+    } catch (IOException e) {
+      log.warn("Failed to load " + fileName + " for " + getProject().getName(), e);
+    } catch (ConfigInvalidException e) {
+      log.warn("Failed to load " + fileName + " for " + getProject().getName(), e);
+    }
+
+    configs.put(fileName, cfg);
+    return cfg;
+  }
+
   public long getMaxObjectSizeLimit() {
     return config.getMaxObjectSizeLimit();
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutDescription.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutDescription.java
index f7dd3cb..58db203 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutDescription.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutDescription.java
@@ -17,7 +17,6 @@
 import com.google.common.base.Objects;
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.DefaultInput;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -57,8 +56,8 @@
   }
 
   @Override
-  public Object apply(ProjectResource resource, Input input)
-      throws AuthException, BadRequestException, ResourceConflictException,
+  public Response<String> apply(ProjectResource resource, Input input)
+      throws AuthException, ResourceConflictException,
       ResourceNotFoundException, IOException {
     if (input == null) {
       input = new Input(); // Delete would set description to null.
@@ -92,8 +91,8 @@
             project.getDescription());
 
         return Strings.isNullOrEmpty(project.getDescription())
-            ? Response.none()
-            : project.getDescription();
+            ? Response.<String>none()
+            : Response.ok(project.getDescription());
       } finally {
         md.close();
       }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutProject.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutProject.java
index 0a96cb5..836899a2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutProject.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutProject.java
@@ -15,12 +15,13 @@
 package com.google.gerrit.server.project;
 
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.project.CreateProject.Input;
 
 public class PutProject implements RestModifyView<ProjectResource, Input> {
   @Override
-  public Object apply(ProjectResource resource, Input input)
+  public Response<?> apply(ProjectResource resource, Input input)
       throws ResourceConflictException {
     throw new ResourceConflictException("Project \"" + resource.getName()
         + "\" already exists");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetDashboard.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetDashboard.java
index 930da12..8319bbd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetDashboard.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetDashboard.java
@@ -19,11 +19,14 @@
 import com.google.gerrit.extensions.restapi.DefaultInput;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.project.SetDashboard.Input;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
+import java.io.IOException;
+
 class SetDashboard implements RestModifyView<DashboardResource, Input> {
   static class Input {
     @DefaultInput
@@ -41,7 +44,7 @@
   @Override
   public Object apply(DashboardResource resource, Input input)
       throws AuthException, BadRequestException, ResourceConflictException,
-      Exception {
+      MethodNotAllowedException, ResourceNotFoundException, IOException {
     if (resource.isProjectDefault()) {
       return defaultSetter.get().apply(resource, input);
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetDefaultDashboard.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetDefaultDashboard.java
index 3f70bc2..a323ec8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetDefaultDashboard.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetDefaultDashboard.java
@@ -36,6 +36,8 @@
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.kohsuke.args4j.Option;
 
+import java.io.IOException;
+
 class SetDefaultDashboard implements RestModifyView<DashboardResource, Input> {
   private final ProjectCache cache;
   private final MetaDataUpdate.Server updateFactory;
@@ -57,9 +59,9 @@
   }
 
   @Override
-  public Object apply(DashboardResource resource, Input input)
+  public Response<DashboardInfo> apply(DashboardResource resource, Input input)
       throws AuthException, BadRequestException, ResourceConflictException,
-      Exception {
+      ResourceNotFoundException, IOException {
     if (input == null) {
       input = new Input(); // Delete would set input to null.
     }
@@ -79,6 +81,8 @@
             IdString.fromUrl(input.id));
       } catch (ResourceNotFoundException e) {
         throw new BadRequestException("dashboard " + input.id + " not found");
+      } catch (ConfigInvalidException e) {
+        throw new ResourceConflictException(e.getMessage());
       }
     }
 
@@ -109,7 +113,7 @@
         if (target != null) {
           DashboardInfo info = get.get().apply(target);
           info.isDefault = true;
-          return info;
+          return Response.ok(info);
         }
         return Response.none();
       } finally {
@@ -136,14 +140,13 @@
     }
 
     @Override
-    public Object apply(ProjectResource resource, Input input)
+    public Response<DashboardInfo> apply(ProjectResource resource, Input input)
         throws AuthException, BadRequestException, ResourceConflictException,
-        Exception {
+        ResourceNotFoundException, IOException {
       SetDefaultDashboard set = setDefault.get();
       set.inherited = inherited;
-      return Response.created(set.apply(
-          DashboardResource.projectDefault(resource.getControl()),
-          input));
+      return set.apply(
+          DashboardResource.projectDefault(resource.getControl()), input);
     }
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
index 999358c..337c6f4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
@@ -19,7 +19,6 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.DefaultInput;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -38,7 +37,7 @@
 
 import java.io.IOException;
 
-class SetParent implements RestModifyView<ProjectResource, Input> {
+public class SetParent implements RestModifyView<ProjectResource, Input> {
   static class Input {
     @DefaultInput
     String parent;
@@ -59,45 +58,18 @@
   }
 
   @Override
-  public String apply(final ProjectResource rsrc, Input input) throws AuthException,
-      BadRequestException, ResourceConflictException,
+  public String apply(final ProjectResource rsrc, Input input)
+      throws AuthException, ResourceConflictException,
       ResourceNotFoundException, UnprocessableEntityException, IOException {
     ProjectControl ctl = rsrc.getControl();
+    validateParentUpdate(ctl, input.parent);
     IdentifiedUser user = (IdentifiedUser) ctl.getCurrentUser();
-    if (!user.getCapabilities().canAdministrateServer()) {
-      throw new AuthException("not administrator");
-    }
-
-    if (rsrc.getNameKey().equals(allProjects)) {
-      throw new ResourceConflictException("cannot set parent of "
-          + allProjects.get());
-    }
-
-    input.parent = Strings.emptyToNull(input.parent);
-    if (input.parent != null) {
-      ProjectState parent = cache.get(new Project.NameKey(input.parent));
-      if (parent == null) {
-        throw new UnprocessableEntityException("parent project " + input.parent
-            + " not found");
-      }
-
-      if (Iterables.tryFind(parent.tree(), new Predicate<ProjectState>() {
-        @Override
-        public boolean apply(ProjectState input) {
-          return input.getProject().getNameKey().equals(rsrc.getNameKey());
-        }
-      }).isPresent()) {
-        throw new ResourceConflictException("cycle exists between "
-            + rsrc.getName() + " and " + parent.getProject().getName());
-      }
-    }
-
     try {
       MetaDataUpdate md = updateFactory.create(rsrc.getNameKey());
       try {
         ProjectConfig config = ProjectConfig.read(md);
         Project project = config.getProject();
-        project.setParentName(input.parent);
+        project.setParentName(Strings.emptyToNull(input.parent));
 
         String msg = Strings.emptyToNull(input.commitMessage);
         if (msg == null) {
@@ -124,4 +96,39 @@
           "invalid project.config: %s", e.getMessage()));
     }
   }
+
+  public void validateParentUpdate(final ProjectControl ctl, String newParent)
+      throws AuthException, ResourceConflictException,
+      UnprocessableEntityException {
+    IdentifiedUser user = (IdentifiedUser) ctl.getCurrentUser();
+    if (!user.getCapabilities().canAdministrateServer()) {
+      throw new AuthException("not administrator");
+    }
+
+    if (ctl.getProject().getNameKey().equals(allProjects)) {
+      throw new ResourceConflictException("cannot set parent of "
+          + allProjects.get());
+    }
+
+    newParent = Strings.emptyToNull(newParent);
+    if (newParent != null) {
+      ProjectState parent = cache.get(new Project.NameKey(newParent));
+      if (parent == null) {
+        throw new UnprocessableEntityException("parent project " + newParent
+            + " not found");
+      }
+
+      if (Iterables.tryFind(parent.tree(), new Predicate<ProjectState>() {
+        @Override
+        public boolean apply(ProjectState input) {
+          return input.getProject().getNameKey()
+              .equals(ctl.getProject().getNameKey());
+        }
+      }).isPresent()) {
+        throw new ResourceConflictException("cycle exists between "
+            + ctl.getProject().getName() + " and "
+            + parent.getProject().getName());
+      }
+    }
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java
index f81b0ca..d1d2cae 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java
@@ -33,8 +33,8 @@
       new ChangeQueryBuilder.Arguments( //
           new InvalidProvider<ReviewDb>(), //
           new InvalidProvider<ChangeQueryRewriter>(), //
-          null, null, null, null, null, //
-          null, null, null, null, null), null);
+          null, null, null, null, null, null, null, //
+          null, null, null, null, null, null, null), null);
 
   static Schema<ChangeData> schema(@Nullable IndexCollection indexes) {
     ChangeIndex index = indexes != null ? indexes.getSearchIndex() : null;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 9fc3dbf..86c0d38 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -32,11 +32,13 @@
 import com.google.gerrit.server.account.GroupBackends;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.SubmitStrategyFactory;
 import com.google.gerrit.server.index.ChangeIndex;
 import com.google.gerrit.server.index.IndexCollection;
 import com.google.gerrit.server.index.Schema;
 import com.google.gerrit.server.patch.PatchListCache;
 import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.ListChildProjects;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.query.IntPredicate;
 import com.google.gerrit.server.query.Predicate;
@@ -84,6 +86,7 @@
   public static final String FIELD_CHANGE = "change";
   public static final String FIELD_COMMENT = "comment";
   public static final String FIELD_COMMIT = "commit";
+  public static final String FIELD_CONFLICTS = "conflicts";
   public static final String FIELD_DRAFTBY = "draftby";
   public static final String FIELD_FILE = "file";
   public static final String FIELD_IS = "is";
@@ -93,6 +96,7 @@
   public static final String FIELD_MESSAGE = "message";
   public static final String FIELD_OWNER = "owner";
   public static final String FIELD_OWNERIN = "ownerin";
+  public static final String FIELD_PARENTPROJECT = "parentproject";
   public static final String FIELD_PROJECT = "project";
   public static final String FIELD_REF = "ref";
   public static final String FIELD_REVIEWER = "reviewer";
@@ -139,6 +143,7 @@
     final Provider<ReviewDb> dbProvider;
     final Provider<ChangeQueryRewriter> rewriter;
     final IdentifiedUser.GenericFactory userFactory;
+    final Provider<CurrentUser> self;
     final CapabilityControl.Factory capabilityControlFactory;
     final ChangeControl.GenericFactory changeControlGenericFactory;
     final AccountResolver accountResolver;
@@ -147,13 +152,17 @@
     final PatchListCache patchListCache;
     final GitRepositoryManager repoManager;
     final ProjectCache projectCache;
+    final Provider<ListChildProjects> listChildProjects;
     final IndexCollection indexes;
+    final SubmitStrategyFactory submitStrategyFactory;
+    final ConflictsCache conflictsCache;
 
     @Inject
     @VisibleForTesting
     public Arguments(Provider<ReviewDb> dbProvider,
         Provider<ChangeQueryRewriter> rewriter,
         IdentifiedUser.GenericFactory userFactory,
+        Provider<CurrentUser> self,
         CapabilityControl.Factory capabilityControlFactory,
         ChangeControl.GenericFactory changeControlGenericFactory,
         AccountResolver accountResolver,
@@ -162,10 +171,14 @@
         PatchListCache patchListCache,
         GitRepositoryManager repoManager,
         ProjectCache projectCache,
-        IndexCollection indexes) {
+        Provider<ListChildProjects> listChildProjects,
+        IndexCollection indexes,
+        SubmitStrategyFactory submitStrategyFactory,
+        ConflictsCache conflictsCache) {
       this.dbProvider = dbProvider;
       this.rewriter = rewriter;
       this.userFactory = userFactory;
+      this.self = self;
       this.capabilityControlFactory = capabilityControlFactory;
       this.changeControlGenericFactory = changeControlGenericFactory;
       this.accountResolver = accountResolver;
@@ -174,7 +187,10 @@
       this.patchListCache = patchListCache;
       this.repoManager = repoManager;
       this.projectCache = projectCache;
+      this.listChildProjects = listChildProjects;
       this.indexes = indexes;
+      this.submitStrategyFactory = submitStrategyFactory;
+      this.conflictsCache = conflictsCache;
     }
   }
 
@@ -218,10 +234,7 @@
           .parse(query));
 
     } else if (PAT_CHANGE_ID.matcher(query).matches()) {
-      if (query.charAt(0) == 'i') {
-        query = "I" + query.substring(1);
-      }
-      return new ChangeIdPredicate(args.dbProvider, query);
+      return new ChangeIdPredicate(args.dbProvider, parseChangeId(query));
     }
 
     throw new IllegalArgumentException();
@@ -308,6 +321,16 @@
   }
 
   @Operator
+  public Predicate<ChangeData> conflicts(String value) throws OrmException,
+      QueryParseException {
+    requireIndex(FIELD_CONFLICTS, value);
+    return new ConflictsPredicate(args.dbProvider, args.patchListCache,
+        args.submitStrategyFactory, args.changeControlGenericFactory,
+        args.userFactory, args.repoManager, args.projectCache,
+        args.conflictsCache, value, parseChange(value));
+  }
+
+  @Operator
   public Predicate<ChangeData> project(String name) {
     if (name.startsWith("^"))
       return new RegexProjectPredicate(args.dbProvider, name);
@@ -315,6 +338,12 @@
   }
 
   @Operator
+  public Predicate<ChangeData> parentproject(String name) {
+    return new ParentProjectPredicate(args.dbProvider, args.projectCache,
+        args.listChildProjects, args.self, name);
+  }
+
+  @Operator
   public Predicate<ChangeData> branch(String name) {
     if (name.startsWith("^"))
       return ref("^" + branchToRef(name.substring(1)));
@@ -665,6 +694,31 @@
     return g;
   }
 
+  private List<Change> parseChange(String value) throws OrmException,
+      QueryParseException {
+    if (PAT_LEGACY_ID.matcher(value).matches()) {
+      return Collections.singletonList(args.dbProvider.get().changes()
+          .get(Change.Id.parse(value)));
+    } else if (PAT_CHANGE_ID.matcher(value).matches()) {
+      Change.Key a = new Change.Key(parseChangeId(value));
+      List<Change> changes =
+          args.dbProvider.get().changes().byKeyRange(a, a.max()).toList();
+      if (changes.isEmpty()) {
+        throw error("Change " + value + " not found");
+      }
+      return changes;
+    }
+
+    throw error("Change " + value + " not found");
+  }
+
+  private static String parseChangeId(String value) {
+    if (value.charAt(0) == 'i') {
+      value = "I" + value.substring(1);
+    }
+    return value;
+  }
+
   private Account.Id self() {
     if (currentUser.isIdentifiedUser()) {
       return ((IdentifiedUser) currentUser).getAccountId();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictKey.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictKey.java
new file mode 100644
index 0000000..e177e37
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictKey.java
@@ -0,0 +1,78 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.change;
+
+import com.google.common.base.Objects;
+import com.google.gerrit.reviewdb.client.Project.SubmitType;
+
+import org.eclipse.jgit.lib.ObjectId;
+
+import java.io.Serializable;
+
+public class ConflictKey implements Serializable {
+  private static final long serialVersionUID = 1L;
+
+  private final ObjectId commit;
+  private final ObjectId otherCommit;
+  private final SubmitType submitType;
+  private final boolean contentMerge;
+
+  public ConflictKey(ObjectId commit, ObjectId otherCommit,
+      SubmitType submitType, boolean contentMerge) {
+    if (SubmitType.FAST_FORWARD_ONLY.equals(submitType)
+        || commit.compareTo(otherCommit) < 0) {
+      this.commit = commit;
+      this.otherCommit = otherCommit;
+    } else {
+      this.commit = otherCommit;
+      this.otherCommit = commit;
+    }
+    this.submitType = submitType;
+    this.contentMerge = contentMerge;
+  }
+
+  public ObjectId getCommit() {
+    return commit;
+  }
+
+  public ObjectId getOtherCommit() {
+    return otherCommit;
+  }
+
+  public SubmitType getSubmitType() {
+    return submitType;
+  }
+
+  public boolean isContentMerge() {
+    return contentMerge;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof ConflictKey)) {
+      return false;
+    }
+    ConflictKey other = (ConflictKey)o;
+    return commit.equals(other.commit)
+        && otherCommit.equals(other.otherCommit)
+        && submitType.equals(other.submitType)
+        && contentMerge == other.contentMerge;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(commit, otherCommit, submitType, contentMerge);
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsCache.java
similarity index 63%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
copy to gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsCache.java
index a5371d2..bf7a5dd 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsCache.java
@@ -12,19 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.change;
+package com.google.gerrit.server.query.change;
 
-import com.google.common.collect.Maps;
+import com.google.gerrit.common.Nullable;
 
-import java.util.Map;
+public interface ConflictsCache {
 
-public class ReviewInput {
-  Map<String, Integer> labels;
+  public void put(ConflictKey key, Boolean value);
 
-  public static ReviewInput approve() {
-    ReviewInput in = new ReviewInput();
-    in.labels = Maps.newHashMap();
-    in.labels.put("Code-Review", 2);
-    return in;
-  }
+  @Nullable
+  public Boolean getIfPresent(ConflictKey key);
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsCacheImpl.java
new file mode 100644
index 0000000..1b3473e
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsCacheImpl.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.change;
+
+import com.google.common.cache.Cache;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+
+@Singleton
+public class ConflictsCacheImpl implements ConflictsCache {
+  public final static String NAME = "conflicts";
+
+  public static Module module() {
+    return new CacheModule() {
+      @Override
+      protected void configure() {
+        persist(NAME, ConflictKey.class, Boolean.class)
+            .maximumWeight(37400);
+        bind(ConflictsCache.class).to(ConflictsCacheImpl.class);
+      }
+    };
+  }
+
+  private final Cache<ConflictKey, Boolean> conflictsCache;
+
+  @Inject
+  public ConflictsCacheImpl(
+      @Named(NAME) Cache<ConflictKey, Boolean> conflictsCache) {
+    this.conflictsCache = conflictsCache;
+  }
+
+  @Override
+  public void put(ConflictKey key, Boolean value) {
+    conflictsCache.put(key, value);
+  }
+
+  @Override
+  public Boolean getIfPresent(ConflictKey key) {
+    return conflictsCache.getIfPresent(key);
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
new file mode 100644
index 0000000..cd94930
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
@@ -0,0 +1,268 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.change;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project.SubmitType;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.git.CodeReviewCommit;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.MergeException;
+import com.google.gerrit.server.git.SubmitStrategy;
+import com.google.gerrit.server.git.SubmitStrategyFactory;
+import com.google.gerrit.server.patch.PatchListCache;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.query.OperatorPredicate;
+import com.google.gerrit.server.query.OrPredicate;
+import com.google.gerrit.server.query.Predicate;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevFlag;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+class ConflictsPredicate extends OrPredicate<ChangeData> {
+  private final String value;
+
+  ConflictsPredicate(Provider<ReviewDb> db, PatchListCache plc,
+      SubmitStrategyFactory submitStrategyFactory,
+      ChangeControl.GenericFactory changeControlFactory,
+      IdentifiedUser.GenericFactory identifiedUserFactory,
+      GitRepositoryManager repoManager, ProjectCache projectCache,
+      ConflictsCache conflictsCache, String value, List<Change> changes)
+      throws OrmException {
+    super(predicates(db, plc, submitStrategyFactory, changeControlFactory,
+        identifiedUserFactory, repoManager, projectCache, conflictsCache,
+        value, changes));
+    this.value = value;
+  }
+
+  private static List<Predicate<ChangeData>> predicates(
+      final Provider<ReviewDb> db, final PatchListCache plc,
+      final SubmitStrategyFactory submitStrategyFactory,
+      final ChangeControl.GenericFactory changeControlFactory,
+      final IdentifiedUser.GenericFactory identifiedUserFactory,
+      final GitRepositoryManager repoManager, final ProjectCache projectCache,
+      final ConflictsCache conflictsCache, final String value,
+      List<Change> changes) throws OrmException {
+    List<Predicate<ChangeData>> changePredicates =
+        Lists.newArrayListWithCapacity(changes.size());
+    for (final Change c : changes) {
+      final ChangeDataCache changeDataCache = new ChangeDataCache(c, db, projectCache);
+      List<String> files = new ChangeData(c).currentFilePaths(db, plc);
+      List<Predicate<ChangeData>> filePredicates =
+          Lists.newArrayListWithCapacity(files.size());
+      for (String file : files) {
+        filePredicates.add(new EqualsFilePredicate(db, plc, file));
+      }
+
+      List<Predicate<ChangeData>> predicatesForOneChange =
+          Lists.newArrayListWithCapacity(5);
+      predicatesForOneChange.add(
+          not(new LegacyChangeIdPredicate(db, c.getId())));
+      predicatesForOneChange.add(
+          new ProjectPredicate(db, c.getProject().get()));
+      predicatesForOneChange.add(
+          new RefPredicate(db, c.getDest().get()));
+      predicatesForOneChange.add(or(filePredicates));
+      predicatesForOneChange.add(new OperatorPredicate<ChangeData>(
+          ChangeQueryBuilder.FIELD_CONFLICTS, value) {
+
+        @Override
+        public boolean match(ChangeData object) throws OrmException {
+          Change otherChange = object.change(db);
+          if (otherChange == null) {
+            return false;
+          }
+          if (!otherChange.getDest().equals(c.getDest())) {
+            return false;
+          }
+          SubmitType submitType = getSubmitType(otherChange, object);
+          if (submitType == null) {
+            return false;
+          }
+          ObjectId other = ObjectId.fromString(
+              object.currentPatchSet(db).getRevision().get());
+          ConflictKey conflictsKey =
+              new ConflictKey(changeDataCache.getTestAgainst(), other, submitType,
+                  changeDataCache.getProjectState().isUseContentMerge());
+          Boolean conflicts = conflictsCache.getIfPresent(conflictsKey);
+          if (conflicts != null) {
+            return conflicts;
+          }
+          try {
+            Repository repo =
+                repoManager.openRepository(otherChange.getProject());
+            try {
+              RevWalk rw = new RevWalk(repo) {
+                @Override
+                protected RevCommit createCommit(AnyObjectId id) {
+                  return new CodeReviewCommit(id);
+                }
+              };
+              try {
+                RevFlag canMergeFlag = rw.newFlag("CAN_MERGE");
+                CodeReviewCommit commit =
+                    (CodeReviewCommit) rw.parseCommit(changeDataCache.getTestAgainst());
+                SubmitStrategy strategy =
+                    submitStrategyFactory.create(submitType,
+                        db.get(), repo, rw, null, canMergeFlag,
+                        getAlreadyAccepted(repo, rw, commit),
+                        otherChange.getDest());
+                CodeReviewCommit otherCommit =
+                    (CodeReviewCommit) rw.parseCommit(other);
+                otherCommit.add(canMergeFlag);
+                conflicts = !strategy.dryRun(commit, otherCommit);
+                conflictsCache.put(conflictsKey, conflicts);
+                return conflicts;
+              } catch (MergeException e) {
+                throw new IllegalStateException(e);
+              } catch (NoSuchProjectException e) {
+                throw new IllegalStateException(e);
+              } finally {
+                rw.release();
+              }
+            } finally {
+              repo.close();
+            }
+          } catch (IOException e) {
+            throw new IllegalStateException(e);
+          }
+        }
+
+        @Override
+        public int getCost() {
+          return 5;
+        }
+
+        private SubmitType getSubmitType(Change change, ChangeData cd) throws OrmException {
+          try {
+            final SubmitTypeRecord r =
+                changeControlFactory.controlFor(change,
+                    identifiedUserFactory.create(change.getOwner()))
+                    .getSubmitTypeRecord(db.get(), cd.currentPatchSet(db), cd);
+            if (r.status != SubmitTypeRecord.Status.OK) {
+              return null;
+            }
+            return r.type;
+          } catch (NoSuchChangeException e) {
+            return null;
+          }
+        }
+
+        private Set<RevCommit> getAlreadyAccepted(Repository repo, RevWalk rw,
+            CodeReviewCommit tip) throws MergeException {
+          Set<RevCommit> alreadyAccepted = Sets.newHashSet();
+
+          if (tip != null) {
+            alreadyAccepted.add(tip);
+          }
+
+          try {
+            for (ObjectId id : changeDataCache.getAlreadyAccepted(repo)) {
+              try {
+                alreadyAccepted.add(rw.parseCommit(id));
+              } catch (IncorrectObjectTypeException iote) {
+                // Not a commit? Skip over it.
+              }
+            }
+          } catch (IOException e) {
+            throw new MergeException(
+                "Failed to determine already accepted commits.", e);
+          }
+
+          return alreadyAccepted;
+        }
+      });
+      changePredicates.add(and(predicatesForOneChange));
+    }
+    return changePredicates;
+  }
+
+  @Override
+  public String toString() {
+    return ChangeQueryBuilder.FIELD_CONFLICTS + ":" + value;
+  }
+
+  private static class ChangeDataCache {
+    private final Change change;
+    private final Provider<ReviewDb> db;
+    private final ProjectCache projectCache;
+
+    private ObjectId testAgainst;
+    private ProjectState projectState;
+    private Set<ObjectId> alreadyAccepted;
+
+    ChangeDataCache(Change change, Provider<ReviewDb> db, ProjectCache projectCache) {
+      this.change = change;
+      this.db = db;
+      this.projectCache = projectCache;
+    }
+
+    ObjectId getTestAgainst()
+        throws OrmException {
+      if (testAgainst == null) {
+        testAgainst = ObjectId.fromString(
+          new ChangeData(change).currentPatchSet(db).getRevision().get());
+      }
+      return testAgainst;
+    }
+
+    ProjectState getProjectState() {
+      if (projectState == null) {
+        projectState = projectCache.get(change.getProject());
+        if (projectState == null) {
+          throw new IllegalStateException(
+              new NoSuchProjectException(change.getProject()));
+        }
+      }
+      return projectState;
+    }
+
+    Set<ObjectId> getAlreadyAccepted(Repository repo) {
+      if (alreadyAccepted == null) {
+        alreadyAccepted = Sets.newHashSet();
+        for (Ref r : repo.getAllRefs().values()) {
+          if (r.getName().startsWith(Constants.R_HEADS)
+              || r.getName().startsWith(Constants.R_TAGS)) {
+            if (r.getObjectId() != null) {
+              alreadyAccepted.add(r.getObjectId());
+            }
+          }
+        }
+      }
+      return alreadyAccepted;
+    }
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsVisibleToPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsVisibleToPredicate.java
index 8992318..27b4994 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsVisibleToPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsVisibleToPredicate.java
@@ -30,7 +30,7 @@
       return ((IdentifiedUser) user).getAccountId().toString();
     }
     if (user instanceof SingleGroupUser) {
-      return "group:" + ((SingleGroupUser) user).getEffectiveGroups().getKnownGroups() //
+      return "group:" + user.getEffectiveGroups().getKnownGroups() //
           .iterator().next().toString();
     }
     return user.toString();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ParentProjectPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ParentProjectPredicate.java
new file mode 100644
index 0000000..2f63f5e
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ParentProjectPredicate.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.change;
+
+import com.google.common.collect.Lists;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.project.ListChildProjects;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectJson.ProjectInfo;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.query.OrPredicate;
+import com.google.gerrit.server.query.Predicate;
+import com.google.inject.Provider;
+
+import java.util.Collections;
+import java.util.List;
+
+class ParentProjectPredicate extends OrPredicate<ChangeData> {
+  private final String value;
+
+  ParentProjectPredicate(Provider<ReviewDb> dbProvider,
+      ProjectCache projectCache, Provider<ListChildProjects> listChildProjects,
+      Provider<CurrentUser> self, String value) {
+    super(predicates(dbProvider, projectCache, listChildProjects, self, value));
+    this.value = value;
+  }
+
+  private static List<Predicate<ChangeData>> predicates(
+      Provider<ReviewDb> dbProvider, ProjectCache projectCache,
+      Provider<ListChildProjects> listChildProjects,
+      Provider<CurrentUser> self, String value) {
+    ProjectState projectState = projectCache.get(new Project.NameKey(value));
+    if (projectState == null) {
+      return Collections.emptyList();
+    }
+
+    List<Predicate<ChangeData>> r = Lists.newArrayList();
+    r.add(new ProjectPredicate(dbProvider, projectState.getProject().getName()));
+    ListChildProjects children = listChildProjects.get();
+    children.setRecursive(true);
+    for (ProjectInfo p : children.apply(new ProjectResource(
+        projectState.controlFor(self.get())))) {
+      r.add(new ProjectPredicate(dbProvider, p.name));
+    }
+    return r;
+  }
+
+  @Override
+  public String toString() {
+    return ChangeQueryBuilder.FIELD_PARENTPROJECT + ":" + value;
+  }
+}
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 4b6a5a6..6ba9e6d 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
@@ -45,7 +45,7 @@
   private boolean reverse;
   private EnumSet<ListChangesOption> options;
 
-  @Option(name = "--query", aliases = {"-q"}, metaVar = "QUERY", multiValued = true, usage = "Query string")
+  @Option(name = "--query", aliases = {"-q"}, metaVar = "QUERY", usage = "Query string")
   private List<String> queries;
 
   @Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "Maximum number of results to return")
@@ -53,7 +53,7 @@
     imp.setLimit(limit);
   }
 
-  @Option(name = "-o", multiValued = true, usage = "Output options per change")
+  @Option(name = "-o", usage = "Output options per change")
   public void addOption(ListChangesOption o) {
     options.add(o);
   }
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 2f3fe54..f6fb4c5 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
@@ -86,7 +86,7 @@
       };
 
   public static enum OutputFormat {
-    TEXT, JSON;
+    TEXT, JSON
   }
 
   private final Gson gson = new Gson();
@@ -114,6 +114,7 @@
   private boolean includeCommitMessage;
   private boolean includeDependencies;
   private boolean includeSubmitRecords;
+  private boolean includeAllReviewers;
 
   private OutputStream outputStream = DisabledOutputStream.INSTANCE;
   private PrintWriter out;
@@ -202,6 +203,10 @@
     includeSubmitRecords = on;
   }
 
+  public void setIncludeAllReviewers(boolean on) {
+    includeAllReviewers = on;
+  }
+
   public void setOutput(OutputStream out, OutputFormat fmt) {
     this.outputStream = out;
     this.outputFormat = fmt;
@@ -309,6 +314,10 @@
           eventFactory.extend(c, d.getChange());
           eventFactory.addTrackingIds(c, d.trackingIds(db));
 
+          if (includeAllReviewers) {
+            eventFactory.addAllReviewers(c, d.getChange());
+          }
+
           if (includeSubmitRecords) {
             PatchSet.Id psId = d.getChange().currentPatchSetId();
             PatchSet patchSet = db.get().patchSets().get(psId);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceProvider.java
index c96ed42..a6e3488 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceProvider.java
@@ -84,7 +84,7 @@
   }
 
   public static enum Context {
-    SINGLE_USER, MULTI_USER;
+    SINGLE_USER, MULTI_USER
   }
 
   private DataSource open(final SitePaths site, final Config cfg,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/ScriptRunner.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/ScriptRunner.java
index 254a780..12c80f4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/ScriptRunner.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/ScriptRunner.java
@@ -37,7 +37,7 @@
 
   static final ScriptRunner NOOP = new ScriptRunner(null, null) {
     void run(final ReviewDb db) {
-    };
+    }
   };
 
   ScriptRunner(final String scriptName, final InputStream script) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/tools/ToolsCatalog.java b/gerrit-server/src/main/java/com/google/gerrit/server/tools/ToolsCatalog.java
index dd874b2..a2b0ad1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/tools/ToolsCatalog.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/tools/ToolsCatalog.java
@@ -140,7 +140,7 @@
   /** A file served out of the tools root directory. */
   public static class Entry {
     public static enum Type {
-      DIR, FILE;
+      DIR, FILE
     }
 
     private final Type type;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java b/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
index 0d0ab1a..07e278a 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
@@ -29,6 +29,8 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.inject.AbstractModule;
+import org.junit.Before;
+import org.junit.Test;
 
 import java.util.Arrays;
 
@@ -46,9 +48,8 @@
   private ProjectConfig local;
   private Util util;
 
-  @Override
+  @Before
   public void setUp() throws Exception {
-    super.setUp();
     util = new Util();
     load("gerrit", "gerrit_common_test.pl", new AbstractModule() {
       @Override
@@ -86,6 +87,7 @@
     env.set(StoredValues.CHANGE_CONTROL, util.user(local).controlFor(change));
   }
 
+  @Test
   public void testGerritCommon() {
     runPrologBasedTests();
   }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java b/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java
index df39003..19edaf4 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java
@@ -29,8 +29,6 @@
 import com.googlecode.prolog_cafe.lang.Term;
 import com.googlecode.prolog_cafe.lang.VariableTerm;
 
-import junit.framework.TestCase;
-
 import java.io.BufferedReader;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -41,9 +39,13 @@
 import java.util.Arrays;
 import java.util.List;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 
 /** Base class for any tests written in Prolog. */
-public abstract class PrologTestCase extends TestCase {
+public abstract class PrologTestCase {
   private static final SymbolTerm test_1 = SymbolTerm.intern("test", 1);
 
   private String pkg;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/StringUtilTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/StringUtilTest.java
index 24f3386..0bbec8a 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/StringUtilTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/StringUtilTest.java
@@ -14,13 +14,16 @@
 
 package com.google.gerrit.server;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
-public class StringUtilTest extends TestCase {
+import static org.junit.Assert.assertEquals;
+
+public class StringUtilTest {
   /**
    * Test the boundary condition that the first character of a string
    * should be escaped.
    */
+  @Test
   public void testEscapeFirstChar() {
     assertEquals(StringUtil.escapeString("\tLeading tab"), "\\tLeading tab");
   }
@@ -29,6 +32,7 @@
    * Test the boundary condition that the last character of a string
    * should be escaped.
    */
+  @Test
   public void testEscapeLastChar() {
     assertEquals(StringUtil.escapeString("Trailing tab\t"), "Trailing tab\\t");
   }
@@ -37,6 +41,7 @@
    * Test that various forms of input strings are escaped (or left as-is)
    * in the expected way.
    */
+  @Test
   public void testEscapeString() {
     final String[] testPairs =
       { "", "",
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
index 18b3c98..d15210d 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
@@ -18,6 +18,9 @@
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.expectLastCall;
 import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
 
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableMap;
@@ -46,9 +49,9 @@
 import com.google.inject.Injector;
 import com.google.inject.TypeLiteral;
 
-import junit.framework.TestCase;
-
 import org.easymock.IAnswer;
+import org.junit.Before;
+import org.junit.Test;
 
 import java.sql.Timestamp;
 import java.util.ArrayList;
@@ -56,7 +59,7 @@
 import java.util.List;
 import java.util.Map;
 
-public class CommentsTest extends TestCase {
+public class CommentsTest {
 
   private Injector injector;
   private RevisionResource revRes1;
@@ -65,8 +68,8 @@
   private PatchLineComment plc2;
   private PatchLineComment plc3;
 
-  @Override
-  protected void setUp() throws Exception {
+  @Before
+  public void setUp() throws Exception {
     @SuppressWarnings("unchecked")
     final DynamicMap<RestView<CommentResource>> views =
         createMock(DynamicMap.class);
@@ -130,6 +133,7 @@
     injector = Guice.createInjector(mod);
   }
 
+  @Test
   public void testListComments() throws Exception {
     // test ListComments for patch set 1
     assertListComments(injector, revRes1, ImmutableMap.of(
@@ -140,6 +144,7 @@
         Collections.<String, ArrayList<PatchLineComment>>emptyMap());
   }
 
+  @Test
   public void testGetComment() throws Exception {
     // test GetComment for existing comment
     assertGetComment(injector, revRes1, plc1, plc1.getKey().get());
@@ -186,9 +191,9 @@
     assertNotNull(actual);
     assertEquals(expected.size(), actual.size());
     assertEquals(expected.keySet(), actual.keySet());
-    for (String filename : expected.keySet()) {
-      List<PatchLineComment> expectedComments = expected.get(filename);
-      List<CommentInfo> actualComments = actual.get(filename);
+    for (Map.Entry<String, ArrayList<PatchLineComment>> entry : expected.entrySet()) {
+      List<PatchLineComment> expectedComments = entry.getValue();
+      List<CommentInfo> actualComments = actual.get(entry.getKey());
       assertNotNull(actualComments);
       assertEquals(expectedComments.size(), actualComments.size());
       for (int i = 0; i < expectedComments.size(); i++) {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/config/ConfigUtilTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/config/ConfigUtilTest.java
index 5d72916..cc19811 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/config/ConfigUtilTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/config/ConfigUtilTest.java
@@ -14,17 +14,19 @@
 
 package com.google.gerrit.server.config;
 
+import org.junit.Test;
+
 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;
-
-import junit.framework.TestCase;
+import static org.junit.Assert.assertEquals;
 
 import java.util.concurrent.TimeUnit;
 
-public class ConfigUtilTest extends TestCase {
+public class ConfigUtilTest {
+  @Test
   public void testTimeUnit() {
     assertEquals(ms(0, MILLISECONDS), parse("0"));
     assertEquals(ms(2, MILLISECONDS), parse("2ms"));
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/config/SitePathsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/config/SitePathsTest.java
index 0087df6..5fdecf0 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/config/SitePathsTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/config/SitePathsTest.java
@@ -14,15 +14,23 @@
 
 package com.google.gerrit.server.config;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import com.google.gerrit.server.util.HostPlatform;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 
-public class SitePathsTest extends TestCase {
+public class SitePathsTest {
+  @Test
   public void testCreate_NotExisting() throws IOException {
     final File root = random();
     final SitePaths site = new SitePaths(root);
@@ -31,6 +39,7 @@
     assertEquals(new File(root, "etc"), site.etc_dir);
   }
 
+  @Test
   public void testCreate_Empty() throws IOException {
     final File root = random();
     try {
@@ -44,6 +53,7 @@
     }
   }
 
+  @Test
   public void testCreate_NonEmpty() throws IOException {
     final File root = random();
     final File txt = new File(root, "test.txt");
@@ -60,6 +70,7 @@
     }
   }
 
+  @Test
   public void testCreate_NotDirectory() throws IOException {
     final File root = random();
     try {
@@ -75,6 +86,7 @@
     }
   }
 
+  @Test
   public void testResolve() throws IOException {
     final File root = random();
     final SitePaths site = new SitePaths(root);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
index 63e62a0..c94186c 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
@@ -26,7 +26,7 @@
         new FakeQueryBuilder.Definition<ChangeData, FakeQueryBuilder>(
           FakeQueryBuilder.class),
         new ChangeQueryBuilder.Arguments(null, null, null, null, null, null,
-          null, null, null, null, null, indexes),
+          null, null, null, null, null, null, null, indexes, null, null),
         null);
   }
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java
index d9016e9..f9eb18f 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java
@@ -19,6 +19,9 @@
 import static com.google.gerrit.reviewdb.client.Change.Status.MERGED;
 import static com.google.gerrit.reviewdb.client.Change.Status.NEW;
 import static com.google.gerrit.reviewdb.client.Change.Status.SUBMITTED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.reviewdb.client.Change;
@@ -31,22 +34,21 @@
 import com.google.gerrit.server.query.change.ChangeQueryBuilder;
 import com.google.gerrit.server.query.change.OrSource;
 import com.google.gerrit.server.query.change.SqlRewriterImpl;
-
-import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Test;
 
 import java.util.EnumSet;
 import java.util.Set;
 
 @SuppressWarnings("unchecked")
-public class IndexRewriteTest extends TestCase {
+public class IndexRewriteTest {
   private FakeIndex index;
   private IndexCollection indexes;
   private ChangeQueryBuilder queryBuilder;
   private IndexRewriteImpl rewrite;
 
-  @Override
+  @Before
   public void setUp() throws Exception {
-    super.setUp();
     index = new FakeIndex(FakeIndex.V2);
     indexes = new IndexCollection();
     indexes.setSearchIndex(index);
@@ -58,26 +60,31 @@
         new SqlRewriterImpl(null));
   }
 
+  @Test
   public void testIndexPredicate() throws Exception {
     Predicate<ChangeData> in = parse("file:a");
     assertEquals(query(in), rewrite(in));
   }
 
+  @Test
   public void testNonIndexPredicate() throws Exception {
     Predicate<ChangeData> in = parse("foo:a");
     assertSame(in, rewrite(in));
   }
 
+  @Test
   public void testIndexPredicates() throws Exception {
     Predicate<ChangeData> in = parse("file:a file:b");
     assertEquals(query(in), rewrite(in));
   }
 
+  @Test
   public void testNonIndexPredicates() throws Exception {
     Predicate<ChangeData> in = parse("foo:a OR foo:b");
     assertEquals(in, rewrite(in));
   }
 
+  @Test
   public void testOneIndexPredicate() throws Exception {
     Predicate<ChangeData> in = parse("foo:a file:b");
     Predicate<ChangeData> out = rewrite(in);
@@ -87,6 +94,7 @@
         out.getChildren());
   }
 
+  @Test
   public void testThreeLevelTreeWithAllIndexPredicates() throws Exception {
     Predicate<ChangeData> in =
         parse("-status:abandoned (status:open OR status:merged)");
@@ -95,6 +103,7 @@
         rewrite.rewrite(in));
   }
 
+  @Test
   public void testThreeLevelTreeWithSomeIndexPredicates() throws Exception {
     Predicate<ChangeData> in = parse("-foo:a (file:b OR file:c)");
     Predicate<ChangeData> out = rewrite(in);
@@ -104,6 +113,7 @@
         out.getChildren());
   }
 
+  @Test
   public void testMultipleIndexPredicates() throws Exception {
     Predicate<ChangeData> in =
         parse("file:a OR foo:b OR file:c OR foo:d");
@@ -115,6 +125,7 @@
         out.getChildren());
   }
 
+  @Test
   public void testIndexAndNonIndexPredicates() throws Exception {
     Predicate<ChangeData> in = parse("status:new bar:p file:a");
     Predicate<ChangeData> out = rewrite(in);
@@ -125,6 +136,7 @@
         out.getChildren());
   }
 
+  @Test
   public void testDuplicateCompoundNonIndexOnlyPredicates() throws Exception {
     Predicate<ChangeData> in =
         parse("(status:new OR status:draft) bar:p file:a");
@@ -136,6 +148,7 @@
         out.getChildren());
   }
 
+  @Test
   public void testDuplicateCompoundIndexOnlyPredicates() throws Exception {
     Predicate<ChangeData> in =
         parse("(status:new OR file:a) bar:p file:b");
@@ -147,6 +160,7 @@
         out.getChildren());
   }
 
+  @Test
   public void testLimit() throws Exception {
     Predicate<ChangeData> in = parse("file:a limit:3");
     Predicate<ChangeData> out = rewrite(in);
@@ -157,6 +171,7 @@
         out.getChildren());
   }
 
+  @Test
   public void testGetPossibleStatus() throws Exception {
     assertEquals(EnumSet.allOf(Change.Status.class), status("file:a"));
     assertEquals(EnumSet.of(NEW), status("is:new"));
@@ -173,6 +188,7 @@
         status("(is:new is:draft) OR (is:merged OR is:submitted)"));
   }
 
+  @Test
   public void testUnsupportedIndexOperator() throws Exception {
     Predicate<ChangeData> in = parse("status:merged file:a");
     assertEquals(query(in), rewrite(in));
@@ -186,6 +202,7 @@
         out.getChildren());
   }
 
+  @Test
   public void testNoChangeIndexUsesSqlRewrites() throws Exception {
     Predicate<ChangeData> in = parse("status:open project:p ref:b");
     Predicate<ChangeData> out;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexedChangeQueryTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexedChangeQueryTest.java
index 81518f5..3d21902 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexedChangeQueryTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexedChangeQueryTest.java
@@ -15,32 +15,35 @@
 package com.google.gerrit.server.index;
 
 import static com.google.gerrit.server.index.IndexedChangeQuery.replaceSortKeyPredicates;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
 
 import com.google.gerrit.server.query.Predicate;
 import com.google.gerrit.server.query.QueryParseException;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeQueryBuilder;
+import org.junit.Before;
+import org.junit.Test;
 
-import junit.framework.TestCase;
-
-public class IndexedChangeQueryTest extends TestCase {
+public class IndexedChangeQueryTest {
   private FakeIndex index;
   private ChangeQueryBuilder queryBuilder;
 
-  @Override
+  @Before
   public void setUp() throws Exception {
-    super.setUp();
     index = new FakeIndex(FakeIndex.V2);
     IndexCollection indexes = new IndexCollection();
     indexes.setSearchIndex(index);
     queryBuilder = new FakeQueryBuilder(indexes);
   }
 
+  @Test
   public void testReplaceSortKeyPredicate_NoSortKey() throws Exception {
     Predicate<ChangeData> p = parse("foo:a bar:b OR (foo:b bar:a)");
     assertSame(p, replaceSortKeyPredicates(p, "1234"));
   }
 
+  @Test
   public void testReplaceSortKeyPredicate_TopLevelSortKey() throws Exception {
     Predicate<ChangeData> p;
     p = parse("foo:a bar:b sortkey_before:1234 OR (foo:b bar:a)");
@@ -51,6 +54,7 @@
         replaceSortKeyPredicates(p, "5678"));
   }
 
+  @Test
   public void testReplaceSortKeyPredicate_NestedSortKey() throws Exception {
     Predicate<ChangeData> p;
     p = parse("foo:a bar:b OR (foo:b bar:a AND sortkey_before:1234)");
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/BasicSerializationTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/BasicSerializationTest.java
index 9b6b0df..622b31e 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/BasicSerializationTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/BasicSerializationTest.java
@@ -14,27 +14,31 @@
 
 package com.google.gerrit.server.ioutil;
 
+import org.junit.Test;
+
 import static com.google.gerrit.server.ioutil.BasicSerialization.readFixInt64;
 import static com.google.gerrit.server.ioutil.BasicSerialization.readString;
 import static com.google.gerrit.server.ioutil.BasicSerialization.readVarInt32;
 import static com.google.gerrit.server.ioutil.BasicSerialization.writeFixInt64;
 import static com.google.gerrit.server.ioutil.BasicSerialization.writeString;
 import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
-
-import junit.framework.TestCase;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
-public class BasicSerializationTest extends TestCase {
+public class BasicSerializationTest {
+  @Test
   public void testReadVarInt32() throws IOException {
     assertEquals(0x00000000, readVarInt32(r(b(0))));
     assertEquals(0x00000003, readVarInt32(r(b(3))));
     assertEquals(0x000000ff, readVarInt32(r(b(0x80 | 0x7f, 0x01))));
   }
 
+  @Test
   public void testWriteVarInt32() throws IOException {
     ByteArrayOutputStream out;
 
@@ -51,6 +55,7 @@
     assertOutput(b(0x80 | 0x7f, 0x01), out);
   }
 
+  @Test
   public void testReadFixInt64() throws IOException {
     assertEquals(0L, readFixInt64(r(b(0, 0, 0, 0, 0, 0, 0, 0))));
     assertEquals(3L, readFixInt64(r(b(0, 0, 0, 0, 0, 0, 0, 3))));
@@ -71,6 +76,7 @@
         0xff, 0xff, 0xff, 0xff))));
   }
 
+  @Test
   public void testWriteFixInt64() throws IOException {
     ByteArrayOutputStream out;
 
@@ -99,6 +105,7 @@
     assertOutput(b(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff), out);
   }
 
+  @Test
   public void testReadString() throws IOException {
     assertNull(readString(r(b(0))));
     assertEquals("a", readString(r(b(1, 'a'))));
@@ -106,6 +113,7 @@
         readString(r(b(7, 'c', 'o', 'f', 'f', 'e', 'e', '4'))));
   }
 
+  @Test
   public void testWriteString() throws IOException {
     ByteArrayOutputStream out;
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/ColumnFormatterTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/ColumnFormatterTest.java
index 3b4005c..02d0582 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/ColumnFormatterTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/ColumnFormatterTest.java
@@ -14,12 +14,13 @@
 
 package com.google.gerrit.server.ioutil;
 
-import junit.framework.TestCase;
+import org.junit.Assert;
+import org.junit.Test;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
 
-public class ColumnFormatterTest extends TestCase {
+public class ColumnFormatterTest {
   /**
    * Holds an in-memory {@link java.io.PrintWriter} object and allows
    * comparisons of its contents to a supplied string via an assert statement.
@@ -35,7 +36,7 @@
 
     public void assertEquals(String str) {
       printWriter.flush();
-      TestCase.assertEquals(stringWriter.toString(), str);
+      Assert.assertEquals(stringWriter.toString(), str);
     }
 
     public PrintWriter getPrintWriter() {
@@ -46,6 +47,7 @@
   /**
    * Test that only lines with at least one column of text emit output.
    */
+  @Test
   public void testEmptyLine() {
     final PrintWriterComparator comparator = new PrintWriterComparator();
     final ColumnFormatter formatter =
@@ -64,6 +66,7 @@
   /**
    * Test that there is no output if no columns are ever added.
    */
+  @Test
   public void testEmptyOutput() {
     final PrintWriterComparator comparator = new PrintWriterComparator();
     final ColumnFormatter formatter =
@@ -78,6 +81,7 @@
    * Test that there is no output (nor any exceptions) if we finalize
    * the output immediately after the creation of the {@link ColumnFormatter}.
    */
+  @Test
   public void testNoNextLine() {
     final PrintWriterComparator comparator = new PrintWriterComparator();
     final ColumnFormatter formatter =
@@ -90,6 +94,7 @@
    * Test that the text in added columns is escaped while the column separator
    * (which of course shouldn't be escaped) is left alone.
    */
+  @Test
   public void testEscapingTakesPlace() {
     final PrintWriterComparator comparator = new PrintWriterComparator();
     final ColumnFormatter formatter =
@@ -106,6 +111,7 @@
    * Test that we get the correct output with multi-line input where the number
    * of columns in each line varies.
    */
+  @Test
   public void testMultiLineDifferentColumnCount() {
     final PrintWriterComparator comparator = new PrintWriterComparator();
     final ColumnFormatter formatter =
@@ -124,6 +130,7 @@
   /**
    * Test that we get the correct output with a single column of input.
    */
+  @Test
   public void testOneColumn() {
     final PrintWriterComparator comparator = new PrintWriterComparator();
     final ColumnFormatter formatter =
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/AddressTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/AddressTest.java
index fbbd72b..02ebf51 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/AddressTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/AddressTest.java
@@ -14,53 +14,65 @@
 
 package com.google.gerrit.server.mail;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
 import java.io.UnsupportedEncodingException;
 
-public class AddressTest extends TestCase {
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+public class AddressTest {
+  @Test
   public void testParse_NameEmail1() {
     final Address a = Address.parse("A U Thor <author@example.com>");
     assertEquals("A U Thor", a.name);
     assertEquals("author@example.com", a.email);
   }
 
+  @Test
   public void testParse_NameEmail2() {
     final Address a = Address.parse("A <a@b>");
     assertEquals("A", a.name);
     assertEquals("a@b", a.email);
   }
 
+  @Test
   public void testParse_NameEmail3() {
     final Address a = Address.parse("<a@b>");
     assertNull(a.name);
     assertEquals("a@b", a.email);
   }
 
+  @Test
   public void testParse_NameEmail4() {
     final Address a = Address.parse("A U Thor<author@example.com>");
     assertEquals("A U Thor", a.name);
     assertEquals("author@example.com", a.email);
   }
 
+  @Test
   public void testParse_NameEmail5() {
     final Address a = Address.parse("A U Thor  <author@example.com>");
     assertEquals("A U Thor", a.name);
     assertEquals("author@example.com", a.email);
   }
 
+  @Test
   public void testParse_Email1() {
     final Address a = Address.parse("author@example.com");
     assertNull(a.name);
     assertEquals("author@example.com", a.email);
   }
 
+  @Test
   public void testParse_Email2() {
     final Address a = Address.parse("a@b");
     assertNull(a.name);
     assertEquals("a@b", a.email);
   }
 
+  @Test
   public void testParseInvalid() {
     assertInvalid("");
     assertInvalid("a");
@@ -88,34 +100,42 @@
     }
   }
 
+  @Test
   public void testToHeaderString_NameEmail1() {
     assertEquals("A <a@a>", format("A", "a@a"));
   }
 
+  @Test
   public void testToHeaderString_NameEmail2() {
     assertEquals("A B <a@a>", format("A B", "a@a"));
   }
 
+  @Test
   public void testToHeaderString_NameEmail3() {
     assertEquals("\"A B. C\" <a@a>", format("A B. C", "a@a"));
   }
 
+  @Test
   public void testToHeaderString_NameEmail4() {
     assertEquals("\"A B, C\" <a@a>", format("A B, C", "a@a"));
   }
 
+  @Test
   public void testToHeaderString_NameEmail5() {
     assertEquals("\"A \\\" C\" <a@a>", format("A \" C", "a@a"));
   }
 
+  @Test
   public void testToHeaderString_NameEmail6() {
     assertEquals("=?UTF-8?Q?A_=E2=82=AC_B?= <a@a>", format("A \u20ac B", "a@a"));
   }
 
+  @Test
   public void testToHeaderString_Email1() {
     assertEquals("a@a", format(null, "a@a"));
   }
 
+  @Test
   public void testToHeaderString_Email2() {
     assertEquals("<a,b@a>", format(null, "a,b@a"));
   }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
index 721059c..af31eaf 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
@@ -19,6 +19,9 @@
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
@@ -27,21 +30,20 @@
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.util.TimeUtil;
 
-import junit.framework.TestCase;
-
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.PersonIdent;
+import org.junit.Before;
+import org.junit.Test;
 
 import java.util.Collections;
 
-public class FromAddressGeneratorProviderTest extends TestCase {
+public class FromAddressGeneratorProviderTest {
   private Config config;
   private PersonIdent ident;
   private AccountCache accountCache;
 
-  @Override
-  protected void setUp() throws Exception {
-    super.setUp();
+  @Before
+  public void setUp() throws Exception {
     config = new Config();
     ident = new PersonIdent("NAME", "e@email", 0, 0);
     accountCache = createStrictMock(AccountCache.class);
@@ -56,10 +58,12 @@
     config.setString("sendemail", null, "from", newFrom);
   }
 
+  @Test
   public void testDefaultIsMIXED() {
     assertTrue(create() instanceof FromAddressGeneratorProvider.PatternGen);
   }
 
+  @Test
   public void testSelectUSER() {
     setFrom("USER");
     assertTrue(create() instanceof FromAddressGeneratorProvider.UserGen);
@@ -71,6 +75,7 @@
     assertTrue(create() instanceof FromAddressGeneratorProvider.UserGen);
   }
 
+  @Test
   public void testUSER_FullyConfiguredUser() {
     setFrom("USER");
 
@@ -86,6 +91,7 @@
     verify(accountCache);
   }
 
+  @Test
   public void testUSER_NoFullNameUser() {
     setFrom("USER");
 
@@ -100,6 +106,7 @@
     verify(accountCache);
   }
 
+  @Test
   public void testUSER_NoPreferredEmailUser() {
     setFrom("USER");
 
@@ -114,6 +121,7 @@
     verify(accountCache);
   }
 
+  @Test
   public void testUSER_NullUser() {
     setFrom("USER");
     replay(accountCache);
@@ -124,6 +132,7 @@
     verify(accountCache);
   }
 
+  @Test
   public void testSelectSERVER() {
     setFrom("SERVER");
     assertTrue(create() instanceof FromAddressGeneratorProvider.ServerGen);
@@ -135,6 +144,7 @@
     assertTrue(create() instanceof FromAddressGeneratorProvider.ServerGen);
   }
 
+  @Test
   public void testSERVER_FullyConfiguredUser() {
     setFrom("SERVER");
 
@@ -150,6 +160,7 @@
     verify(accountCache);
   }
 
+  @Test
   public void testSERVER_NullUser() {
     setFrom("SERVER");
     replay(accountCache);
@@ -160,6 +171,7 @@
     verify(accountCache);
   }
 
+  @Test
   public void testSelectMIXED() {
     setFrom("MIXED");
     assertTrue(create() instanceof FromAddressGeneratorProvider.PatternGen);
@@ -171,6 +183,7 @@
     assertTrue(create() instanceof FromAddressGeneratorProvider.PatternGen);
   }
 
+  @Test
   public void testMIXED_FullyConfiguredUser() {
     setFrom("MIXED");
 
@@ -186,6 +199,7 @@
     verify(accountCache);
   }
 
+  @Test
   public void testMIXED_NoFullNameUser() {
     setFrom("MIXED");
 
@@ -200,6 +214,7 @@
     verify(accountCache);
   }
 
+  @Test
   public void testMIXED_NoPreferredEmailUser() {
     setFrom("MIXED");
 
@@ -214,6 +229,7 @@
     verify(accountCache);
   }
 
+  @Test
   public void testMIXED_NullUser() {
     setFrom("MIXED");
     replay(accountCache);
@@ -224,6 +240,7 @@
     verify(accountCache);
   }
 
+  @Test
   public void testCUSTOM_FullyConfiguredUser() {
     setFrom("A ${user} B <my.server@email.address>");
 
@@ -239,6 +256,7 @@
     verify(accountCache);
   }
 
+  @Test
   public void testCUSTOM_NoFullNameUser() {
     setFrom("A ${user} B <my.server@email.address>");
 
@@ -253,6 +271,7 @@
     verify(accountCache);
   }
 
+  @Test
   public void testCUSTOM_NullUser() {
     setFrom("A ${user} B <my.server@email.address>");
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/patch/PatchListEntryTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/patch/PatchListEntryTest.java
index 7df0696..af60ea8 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/patch/PatchListEntryTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/patch/PatchListEntryTest.java
@@ -12,14 +12,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-
 package com.google.gerrit.server.patch;
 
 import com.google.gerrit.reviewdb.client.Patch;
+import org.junit.Test;
 
-import junit.framework.TestCase;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 
-public class PatchListEntryTest extends TestCase {
+public class PatchListEntryTest {
+  @Test
   public void testEmpty1() {
     final String name = "empty-file";
     final PatchListEntry e = PatchListEntry.empty(name);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
index 8f4e058..79929fc 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
@@ -26,6 +26,8 @@
 import static com.google.gerrit.server.project.Util.DEVS;
 import static com.google.gerrit.server.project.Util.grant;
 import static com.google.gerrit.server.project.Util.doNotInherit;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import com.google.gerrit.common.data.Capable;
 import com.google.gerrit.common.data.PermissionRange;
@@ -33,10 +35,10 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.git.ProjectConfig;
+import org.junit.Before;
+import org.junit.Test;
 
-import junit.framework.TestCase;
-
-public class RefControlTest extends TestCase {
+public class RefControlTest {
   private static void assertOwner(String ref, ProjectControl u) {
     assertTrue("OWN " + ref, u.controlForRef(ref).isOwner());
   }
@@ -54,14 +56,14 @@
     util = new Util();
   }
 
-  @Override
+  @Before
   public void setUp() throws Exception {
-    super.setUp();
     local = new ProjectConfig(localKey);
     local.createInMemory();
     util.add(local);
   }
 
+  @Test
   public void testOwnerProject() {
     grant(local, OWNER, ADMIN, "refs/*");
 
@@ -72,6 +74,7 @@
     assertTrue("is owner", uAdmin.isOwner());
   }
 
+  @Test
   public void testBranchDelegation1() {
     grant(local, OWNER, ADMIN, "refs/*");
     grant(local, OWNER, DEVS, "refs/heads/x/*");
@@ -88,6 +91,7 @@
     assertNotOwner("refs/heads/master", uDev);
   }
 
+  @Test
   public void testBranchDelegation2() {
     grant(local, OWNER, ADMIN, "refs/*");
     grant(local, OWNER, DEVS, "refs/heads/x/*");
@@ -116,6 +120,7 @@
     assertNotOwner("refs/heads/master", uFix);
   }
 
+  @Test
   public void testInheritRead_SingleBranchDeniesUpload() {
     grant(util.getParentConfig(), READ, REGISTERED, "refs/*");
     grant(util.getParentConfig(), PUSH, REGISTERED, "refs/for/refs/*");
@@ -133,6 +138,7 @@
         u.controlForRef("refs/heads/foobar").canUpload());
   }
 
+  @Test
   public void testInheritRead_SingleBranchDoesNotOverrideInherited() {
     grant(util.getParentConfig(), READ, REGISTERED, "refs/*");
     grant(util.getParentConfig(), PUSH, REGISTERED, "refs/for/refs/*");
@@ -148,6 +154,7 @@
         u.controlForRef("refs/heads/foobar").canUpload());
   }
 
+  @Test
   public void testInheritDuplicateSections() {
     grant(util.getParentConfig(), READ, ADMIN, "refs/*");
     grant(local, READ, DEVS, "refs/heads/*");
@@ -160,6 +167,7 @@
     assertTrue("d can read", util.user(local, "d", DEVS).isVisible());
   }
 
+  @Test
   public void testInheritRead_OverrideWithDeny() {
     grant(util.getParentConfig(), READ, REGISTERED, "refs/*");
     grant(local, READ, REGISTERED, "refs/*").setDeny();
@@ -168,6 +176,7 @@
     assertFalse("can't read", u.isVisible());
   }
 
+  @Test
   public void testInheritRead_AppendWithDenyOfRef() {
     grant(util.getParentConfig(), READ, REGISTERED, "refs/*");
     grant(local, READ, REGISTERED, "refs/heads/*").setDeny();
@@ -179,6 +188,7 @@
     assertTrue("no master", u.controlForRef("refs/heads/master").isVisible());
   }
 
+  @Test
   public void testInheritRead_OverridesAndDeniesOfRef() {
     grant(util.getParentConfig(), READ, REGISTERED, "refs/*");
     grant(local, READ, REGISTERED, "refs/*").setDeny();
@@ -191,6 +201,7 @@
     assertTrue("can read", u.controlForRef("refs/heads/foobar").isVisible());
   }
 
+  @Test
   public void testInheritSubmit_OverridesAndDeniesOfRef() {
     grant(util.getParentConfig(), SUBMIT, REGISTERED, "refs/*");
     grant(local, SUBMIT, REGISTERED, "refs/*").setDeny();
@@ -202,6 +213,7 @@
     assertTrue("can submit", u.controlForRef("refs/heads/foobar").canSubmit());
   }
 
+  @Test
   public void testCannotUploadToAnyRef() {
     grant(util.getParentConfig(), READ, REGISTERED, "refs/*");
     grant(local, READ, DEVS, "refs/heads/*");
@@ -213,6 +225,7 @@
         u.controlForRef("refs/heads/master").canUpload());
   }
 
+  @Test
   public void testUsernamePatternNonRegex() {
     grant(local, READ, DEVS, "refs/sb/${username}/heads/*");
 
@@ -221,6 +234,7 @@
     assertTrue("d can read", d.controlForRef("refs/sb/d/heads/foobar").isVisible());
   }
 
+  @Test
   public void testUsernamePatternWithRegex() {
     grant(local, READ, DEVS, "^refs/sb/${username}/heads/.*");
 
@@ -229,6 +243,7 @@
     assertTrue("d can read", d.controlForRef("refs/sb/dev/heads/foobar").isVisible());
   }
 
+  @Test
   public void testSortWithRegex() {
     grant(local, READ, DEVS, "^refs/heads/.*");
     grant(util.getParentConfig(), READ, ANONYMOUS, "^refs/heads/.*-QA-.*");
@@ -238,6 +253,7 @@
     assertTrue("d can read", d.controlForRef("refs/heads/foo-QA-bar").isVisible());
   }
 
+  @Test
   public void testBlockRule_ParentBlocksChild() {
     grant(local, PUSH, DEVS, "refs/tags/*");
     grant(util.getParentConfig(), PUSH, ANONYMOUS, "refs/tags/*").setBlock();
@@ -246,6 +262,7 @@
     assertFalse("u can't force update tag", u.controlForRef("refs/tags/V10").canForceUpdate());
   }
 
+  @Test
   public void testBlockLabelRange_ParentBlocksChild() {
     grant(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
     grant(util.getParentConfig(), LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*").setBlock();
@@ -259,6 +276,7 @@
     assertFalse("u can't vote 2", range.contains(2));
   }
 
+  @Test
   public void testUnblockNoForce() {
     grant(local, PUSH, ANONYMOUS, "refs/heads/*").setBlock();
     grant(local, PUSH, DEVS, "refs/heads/*");
@@ -267,6 +285,7 @@
     assertTrue("u can push", u.controlForRef("refs/heads/master").canUpdate());
   }
 
+  @Test
   public void testUnblockForce() {
     PermissionRule r = grant(local, PUSH, ANONYMOUS, "refs/heads/*");
     r.setBlock();
@@ -277,6 +296,7 @@
     assertTrue("u can force push", u.controlForRef("refs/heads/master").canForceUpdate());
   }
 
+  @Test
   public void testUnblockForceWithAllowNoForce_NotPossible() {
     PermissionRule r = grant(local, PUSH, ANONYMOUS, "refs/heads/*");
     r.setBlock();
@@ -287,6 +307,7 @@
     assertFalse("u can't force push", u.controlForRef("refs/heads/master").canForceUpdate());
   }
 
+  @Test
   public void testUnblockMoreSpecificRef_Fails() {
     grant(local, PUSH, ANONYMOUS, "refs/heads/*").setBlock();
     grant(local, PUSH, DEVS, "refs/heads/master");
@@ -295,6 +316,7 @@
     assertFalse("u can't push", u.controlForRef("refs/heads/master").canUpdate());
   }
 
+  @Test
   public void testUnblockLargerScope_Fails() {
     grant(local, PUSH, ANONYMOUS, "refs/heads/master").setBlock();
     grant(local, PUSH, DEVS, "refs/heads/*");
@@ -303,6 +325,7 @@
     assertFalse("u can't push", u.controlForRef("refs/heads/master").canUpdate());
   }
 
+  @Test
   public void testUnblockInLocal_Fails() {
     grant(util.getParentConfig(), PUSH, ANONYMOUS, "refs/heads/*").setBlock();
     grant(local, PUSH, fixers, "refs/heads/*");
@@ -311,6 +334,7 @@
     assertFalse("u can't push", f.controlForRef("refs/heads/master").canUpdate());
   }
 
+  @Test
   public void testUnblockInParentBlockInLocal() {
     grant(util.getParentConfig(), PUSH, ANONYMOUS, "refs/heads/*").setBlock();
     grant(util.getParentConfig(), PUSH, DEVS, "refs/heads/*");
@@ -320,6 +344,7 @@
     assertFalse("u can't push", d.controlForRef("refs/heads/master").canUpdate());
   }
 
+  @Test
   public void testUnblockVisibilityByREGISTEREDUsers() {
     grant(local, READ, ANONYMOUS, "refs/heads/*").setBlock();
     grant(local, READ, REGISTERED, "refs/heads/*");
@@ -328,6 +353,7 @@
     assertTrue("u can read", u.controlForRef("refs/heads/master").isVisibleByRegisteredUsers());
   }
 
+  @Test
   public void testUnblockInLocalVisibilityByRegisteredUsers_Fails() {
     grant(util.getParentConfig(), READ, ANONYMOUS, "refs/heads/*").setBlock();
     grant(local, READ, REGISTERED, "refs/heads/*");
@@ -336,6 +362,7 @@
     assertFalse("u can't read", u.controlForRef("refs/heads/master").isVisibleByRegisteredUsers());
   }
 
+  @Test
   public void testUnblockForceEditTopicName() {
     grant(local, EDIT_TOPIC_NAME, ANONYMOUS, "refs/heads/*").setBlock();
     grant(local, EDIT_TOPIC_NAME, DEVS, "refs/heads/*").setForce(true);
@@ -345,6 +372,7 @@
         .canForceEditTopicName());
   }
 
+  @Test
   public void testUnblockInLocalForceEditTopicName_Fails() {
     grant(util.getParentConfig(), EDIT_TOPIC_NAME, ANONYMOUS, "refs/heads/*")
         .setBlock();
@@ -355,6 +383,7 @@
         .canForceEditTopicName());
   }
 
+  @Test
   public void testUnblockRange() {
     grant(local, LABEL + "Code-Review", -1, +1, ANONYMOUS, "refs/heads/*").setBlock();
     grant(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
@@ -365,6 +394,7 @@
     assertTrue("u can vote +2", range.contains(2));
   }
 
+  @Test
   public void testUnblockRangeOnMoreSpecificRef_Fails() {
     grant(local, LABEL + "Code-Review", -1, +1, ANONYMOUS, "refs/heads/*").setBlock();
     grant(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/master");
@@ -375,6 +405,7 @@
     assertFalse("u can't vote +2", range.contains(-2));
   }
 
+  @Test
   public void testUnblockRangeOnLargerScope_Fails() {
     grant(local, LABEL + "Code-Review", -1, +1, ANONYMOUS, "refs/heads/master").setBlock();
     grant(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
@@ -385,6 +416,7 @@
     assertFalse("u can't vote +2", range.contains(-2));
   }
 
+  @Test
   public void testUnblockInLocalRange_Fails() {
     grant(util.getParentConfig(), LABEL + "Code-Review", -1, 1, ANONYMOUS,
         "refs/heads/*").setBlock();
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
index a99eba1..c6e621a 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
@@ -54,12 +54,12 @@
 import java.util.Set;
 
 public class Util {
-  public static AccountGroup.UUID ANONYMOUS = AccountGroup.ANONYMOUS_USERS;
-  public static AccountGroup.UUID REGISTERED = AccountGroup.REGISTERED_USERS;
-  public static AccountGroup.UUID ADMIN = new AccountGroup.UUID("test.admin");
-  public static AccountGroup.UUID DEVS = new AccountGroup.UUID("test.devs");
+  public static final AccountGroup.UUID ANONYMOUS = AccountGroup.ANONYMOUS_USERS;
+  public static final AccountGroup.UUID REGISTERED = AccountGroup.REGISTERED_USERS;
+  public static final AccountGroup.UUID ADMIN = new AccountGroup.UUID("test.admin");
+  public static final AccountGroup.UUID DEVS = new AccountGroup.UUID("test.devs");
 
-  public static LabelType CR = category("Code-Review",
+  public static final LabelType CR = category("Code-Review",
       value(2, "Looks good to me, approved"),
       value(1, "Looks good to me, but someone else must approve"),
       value(0, "No score"),
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/AndPredicateTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/AndPredicateTest.java
index 0ec23d5..9b3a331 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/AndPredicateTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/AndPredicateTest.java
@@ -15,14 +15,19 @@
 package com.google.gerrit.server.query;
 
 import static com.google.gerrit.server.query.Predicate.and;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-public class AndPredicateTest extends TestCase {
+public class AndPredicateTest {
   private static final class TestPredicate extends OperatorPredicate<String> {
     private TestPredicate(String name, String value) {
       super(name, value);
@@ -43,6 +48,7 @@
     return new TestPredicate(name, value);
   }
 
+  @Test
   @SuppressWarnings("unchecked")
   public void testChildren() {
     final TestPredicate a = f("author", "alice");
@@ -53,6 +59,7 @@
     assertSame(b, n.getChild(1));
   }
 
+  @Test
   @SuppressWarnings("unchecked")
   public void testChildrenUnmodifiable() {
     final TestPredicate a = f("author", "alice");
@@ -83,6 +90,7 @@
     assertEquals(o + " did not affect child", l, p.getChildren());
   }
 
+  @Test
   @SuppressWarnings("unchecked")
   public void testToString() {
     final TestPredicate a = f("q", "alice");
@@ -92,6 +100,7 @@
     assertEquals("(q:alice q:bob q:charlie)", and(a, b, c).toString());
   }
 
+  @Test
   @SuppressWarnings("unchecked")
   public void testEquals() {
     final TestPredicate a = f("author", "alice");
@@ -107,6 +116,7 @@
     assertFalse(and(a, c).equals(a));
   }
 
+  @Test
   @SuppressWarnings("unchecked")
   public void testHashCode() {
     final TestPredicate a = f("author", "alice");
@@ -118,6 +128,7 @@
     assertFalse(and(a, c).hashCode() == and(a, b).hashCode());
   }
 
+  @Test
   @SuppressWarnings("unchecked")
   public void testCopy() {
     final TestPredicate a = f("author", "alice");
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/FieldPredicateTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/FieldPredicateTest.java
index a37a336..e31caaf 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/FieldPredicateTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/FieldPredicateTest.java
@@ -14,11 +14,16 @@
 
 package com.google.gerrit.server.query;
 
-import junit.framework.TestCase;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
 
 import java.util.Collections;
 
-public class FieldPredicateTest extends TestCase {
+public class FieldPredicateTest {
   private static final class TestPredicate extends OperatorPredicate<String> {
     private TestPredicate(String name, String value) {
       super(name, value);
@@ -39,12 +44,14 @@
     return new TestPredicate(name, value);
   }
 
+  @Test
   public void testToString() {
     assertEquals("author:bob", f("author", "bob").toString());
     assertEquals("author:\"\"", f("author", "").toString());
     assertEquals("owner:\"A U Thor\"", f("owner", "A U Thor").toString());
   }
 
+  @Test
   public void testEquals() {
     assertTrue(f("author", "bob").equals(f("author", "bob")));
     assertFalse(f("author", "bob").equals(f("author", "alice")));
@@ -52,11 +59,13 @@
     assertFalse(f("author", "bob").equals("author"));
   }
 
+  @Test
   public void testHashCode() {
     assertTrue(f("a", "bob").hashCode() == f("a", "bob").hashCode());
     assertFalse(f("a", "bob").hashCode() == f("a", "alice").hashCode());
   }
 
+  @Test
   public void testNameValue() {
     final String name = "author";
     final String value = "alice";
@@ -66,6 +75,7 @@
     assertEquals(0, f.getChildren().size());
   }
 
+  @Test
   public void testCopy() {
     final OperatorPredicate<String> f = f("author", "alice");
     assertSame(f, f.copy(Collections.<Predicate<String>> emptyList()));
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/NotPredicateTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/NotPredicateTest.java
index 90b9ca7..9df906c 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/NotPredicateTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/NotPredicateTest.java
@@ -16,13 +16,18 @@
 
 import static com.google.gerrit.server.query.Predicate.and;
 import static com.google.gerrit.server.query.Predicate.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
 import java.util.Collections;
 import java.util.List;
 
-public class NotPredicateTest extends TestCase {
+public class NotPredicateTest {
   private static final class TestPredicate extends OperatorPredicate<String> {
     private TestPredicate(String name, String value) {
       super(name, value);
@@ -43,6 +48,7 @@
     return new TestPredicate(name, value);
   }
 
+  @Test
   public void testNotNot() {
     final TestPredicate p = f("author", "bob");
     final Predicate<String> n = not(p);
@@ -51,6 +57,7 @@
     assertSame(p, not(n));
   }
 
+  @Test
   public void testChildren() {
     final TestPredicate p = f("author", "bob");
     final Predicate<String> n = not(p);
@@ -58,6 +65,7 @@
     assertSame(p, n.getChild(0));
   }
 
+  @Test
   public void testChildrenUnmodifiable() {
     final TestPredicate p = f("author", "bob");
     final Predicate<String> n = not(p);
@@ -87,10 +95,12 @@
     assertSame(o + " did not affect child", c, p.getChild(0));
   }
 
+  @Test
   public void testToString() {
     assertEquals("-author:bob", not(f("author", "bob")).toString());
   }
 
+  @Test
   public void testEquals() {
     assertTrue(not(f("author", "bob")).equals(not(f("author", "bob"))));
     assertFalse(not(f("author", "bob")).equals(not(f("author", "alice"))));
@@ -98,11 +108,13 @@
     assertFalse(not(f("author", "bob")).equals("author"));
   }
 
+  @Test
   public void testHashCode() {
     assertTrue(not(f("a", "b")).hashCode() == not(f("a", "b")).hashCode());
     assertFalse(not(f("a", "b")).hashCode() == not(f("a", "a")).hashCode());
   }
 
+  @Test
   @SuppressWarnings({"rawtypes", "unchecked"})
   public void testCopy() {
     final TestPredicate a = f("author", "alice");
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/OrPredicateTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/OrPredicateTest.java
index 7f3ce50..27696bb 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/OrPredicateTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/OrPredicateTest.java
@@ -15,14 +15,19 @@
 package com.google.gerrit.server.query;
 
 import static com.google.gerrit.server.query.Predicate.or;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-public class OrPredicateTest extends TestCase {
+public class OrPredicateTest {
   private static final class TestPredicate extends OperatorPredicate<String> {
     private TestPredicate(String name, String value) {
       super(name, value);
@@ -43,6 +48,7 @@
     return new TestPredicate(name, value);
   }
 
+  @Test
   @SuppressWarnings("unchecked")
   public void testChildren() {
     final TestPredicate a = f("author", "alice");
@@ -53,6 +59,7 @@
     assertSame(b, n.getChild(1));
   }
 
+  @Test
   @SuppressWarnings("unchecked")
   public void testChildrenUnmodifiable() {
     final TestPredicate a = f("author", "alice");
@@ -83,6 +90,7 @@
     assertEquals(o + " did not affect child", l, p.getChildren());
   }
 
+  @Test
   @SuppressWarnings("unchecked")
   public void testToString() {
     final TestPredicate a = f("q", "alice");
@@ -92,6 +100,7 @@
     assertEquals("(q:alice OR q:bob OR q:charlie)", or(a, b, c).toString());
   }
 
+  @Test
   @SuppressWarnings("unchecked")
   public void testEquals() {
     final TestPredicate a = f("author", "alice");
@@ -107,6 +116,7 @@
     assertFalse(or(a, c).equals(a));
   }
 
+  @Test
   @SuppressWarnings("unchecked")
   public void testHashCode() {
     final TestPredicate a = f("author", "alice");
@@ -118,6 +128,7 @@
     assertFalse(or(a, c).hashCode() == or(a, b).hashCode());
   }
 
+  @Test
   @SuppressWarnings("unchecked")
   public void testCopy() {
     final TestPredicate a = f("author", "alice");
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/QueryParserTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/QueryParserTest.java
index 9534d2b..0eca069 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/QueryParserTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/QueryParserTest.java
@@ -13,13 +13,13 @@
 // limitations under the License.
 
 package com.google.gerrit.server.query;
-
-
-import junit.framework.TestCase;
-
 import org.antlr.runtime.tree.Tree;
+import org.junit.Test;
 
-public class QueryParserTest extends TestCase {
+import static org.junit.Assert.assertEquals;
+
+public class QueryParserTest {
+  @Test
   public void testProjectBare() throws QueryParseException {
     Tree r;
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractIndexQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractIndexQueryChangesTest.java
index 5dacd49..5099d10 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractIndexQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractIndexQueryChangesTest.java
@@ -18,10 +18,10 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.change.ChangeInserter;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.PostReview;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.project.ChangeControl;
 
@@ -70,13 +70,13 @@
     Change change = ins.insert();
     ChangeControl ctl = changeControlFactory.controlFor(change, user);
 
-    PostReview.Input input = new PostReview.Input();
+    ReviewInput input = new ReviewInput();
     input.message = "toplevel";
-    PostReview.Comment comment = new PostReview.Comment();
+    ReviewInput.Comment comment = new ReviewInput.Comment();
     comment.line = 1;
     comment.message = "inline";
-    input.comments = ImmutableMap.<String, List<PostReview.Comment>> of(
-        "Foo.java", ImmutableList.<PostReview.Comment> of(comment));
+    input.comments = ImmutableMap.<String, List<ReviewInput.Comment>> of(
+        "Foo.java", ImmutableList.<ReviewInput.Comment> of(comment));
     postReview.apply(new RevisionResource(
         new ChangeResource(ctl), ins.getPatchSet()), input);
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 1e8b87a..2edb955 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -26,6 +26,7 @@
 import com.google.common.collect.Lists;
 import com.google.common.hash.Hashing;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.lifecycle.LifecycleManager;
 import com.google.gerrit.reviewdb.client.Account;
@@ -359,7 +360,7 @@
     Change change = ins.insert();
     ChangeControl ctl = changeControlFactory.controlFor(change, user);
 
-    PostReview.Input input = new PostReview.Input();
+    ReviewInput input = new ReviewInput();
     input.message = "toplevel";
     input.labels = ImmutableMap.<String, Short> of("Code-Review", (short) 1);
     postReview.apply(new RevisionResource(
@@ -476,7 +477,7 @@
     assertResultEquals(change2, results.get(0));
     assertResultEquals(change1, results.get(1));
 
-    PostReview.Input input = new PostReview.Input();
+    ReviewInput input = new ReviewInput();
     input.message = "toplevel";
     postReview.apply(new RevisionResource(
         new ChangeResource(ctl1), ins1.getPatchSet()), input);
@@ -509,7 +510,7 @@
     assertResultEquals(change2, results.get(0));
     assertResultEquals(change1, results.get(1));
 
-    PostReview.Input input = new PostReview.Input();
+    ReviewInput input = new ReviewInput();
     input.message = "toplevel";
     postReview.apply(new RevisionResource(
         new ChangeResource(ctl1), ins1.getPatchSet()), input);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/RegexFilePredicateTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/RegexFilePredicateTest.java
index 1500272..85b0311 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/RegexFilePredicateTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/RegexFilePredicateTest.java
@@ -16,12 +16,15 @@
 
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gwtorm.server.OrmException;
-
-import junit.framework.TestCase;
+import org.junit.Test;
 
 import java.util.Arrays;
 
-public class RegexFilePredicateTest extends TestCase {
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class RegexFilePredicateTest {
+  @Test
   public void testPrefixOnlyOptimization() throws OrmException {
     RegexFilePredicate p = predicate("^a/b/.*");
     assertTrue(p.match(change("a/b/source.c")));
@@ -31,6 +34,7 @@
     assertFalse(p.match(change("a/bb/source.c")));
   }
 
+  @Test
   public void testPrefixReducesSearchSpace() throws OrmException {
     RegexFilePredicate p = predicate("^a/b/.*\\.[ch]");
     assertTrue(p.match(change("a/b/source.c")));
@@ -40,6 +44,7 @@
     assertTrue(p.match(change("a/b/a.a", "a/b/a.d", "a/b/a.h")));
   }
 
+  @Test
   public void testFileExtension_Constant() throws OrmException {
     RegexFilePredicate p = predicate("^.*\\.res");
     assertTrue(p.match(change("test.res")));
@@ -47,6 +52,7 @@
     assertFalse(p.match(change("test.res.bar")));
   }
 
+  @Test
   public void testFileExtension_CharacterGroup() throws OrmException {
     RegexFilePredicate p = predicate("^.*\\.[ch]");
     assertTrue(p.match(change("test.c")));
@@ -54,6 +60,7 @@
     assertFalse(p.match(change("test.C")));
   }
 
+  @Test
   public void testEndOfString() throws OrmException {
     assertTrue(predicate("^a$").match(change("a")));
     assertFalse(predicate("^a$").match(change("a$")));
@@ -62,6 +69,7 @@
     assertTrue(predicate("^a\\$").match(change("a$")));
   }
 
+  @Test
   public void testExactMatch() throws OrmException {
     RegexFilePredicate p = predicate("^foo.c");
     assertTrue(p.match(change("foo.c")));
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java
index e58266f..4756390 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java
@@ -14,6 +14,11 @@
 
 package com.google.gerrit.server.schema;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
@@ -29,9 +34,10 @@
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
-import junit.framework.TestCase;
-
 import org.eclipse.jgit.lib.Repository;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
 import java.io.File;
 import java.io.IOException;
@@ -40,7 +46,7 @@
 import java.util.Arrays;
 import java.util.List;
 
-public class SchemaCreatorTest extends TestCase {
+public class SchemaCreatorTest {
   @Inject
   private AllProjectsName allProjects;
 
@@ -50,18 +56,17 @@
   @Inject
   private InMemoryDatabase db;
 
-  @Override
-  protected void setUp() throws Exception {
-    super.setUp();
+  @Before
+  public void setUp() throws Exception {
     new InMemoryModule().inject(this);
   }
 
-  @Override
-  protected void tearDown() throws Exception {
+  @After
+  public void tearDown() throws Exception {
     InMemoryDatabase.drop(db);
-    super.tearDown();
   }
 
+  @Test
   public void testGetCauses_CreateSchema() throws OrmException, SQLException,
       IOException {
     // Initially the schema should be empty.
@@ -109,6 +114,7 @@
     }
   }
 
+  @Test
   public void testCreateSchema_LabelTypes() throws Exception {
     List<String> labels = Lists.newArrayList();
     for (LabelType label : getLabelTypes().getLabelTypes()) {
@@ -117,6 +123,7 @@
     assertEquals(ImmutableList.of("Code-Review"), labels);
   }
 
+  @Test
   public void testCreateSchema_Label_CodeReview() throws Exception {
     LabelType codeReview = getLabelTypes().byLabel("Code-Review");
     assertNotNull(codeReview);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
index 5039cc2..e6b7a3f 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
@@ -34,10 +34,11 @@
 import com.google.inject.Guice;
 import com.google.inject.TypeLiteral;
 
-import junit.framework.TestCase;
-
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.PersonIdent;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -45,21 +46,22 @@
 import java.util.List;
 import java.util.UUID;
 
-public class SchemaUpdaterTest extends TestCase {
+import static org.junit.Assert.assertEquals;
+
+public class SchemaUpdaterTest {
   private InMemoryDatabase db;
 
-  @Override
-  protected void setUp() throws Exception {
-    super.setUp();
+  @Before
+  public void setUp() throws Exception {
     db = InMemoryDatabase.newDatabase();
   }
 
-  @Override
-  protected void tearDown() throws Exception {
+  @After
+  public void tearDown() throws Exception {
     InMemoryDatabase.drop(db);
-    super.tearDown();
   }
 
+  @Test
   public void testUpdate() throws OrmException, FileNotFoundException,
       IOException {
     db.create();
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/util/IdGeneratorTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/util/IdGeneratorTest.java
index 1f50fa2..5546410 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/util/IdGeneratorTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/util/IdGeneratorTest.java
@@ -14,11 +14,15 @@
 
 package com.google.gerrit.server.util;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
 import java.util.HashSet;
 
-public class IdGeneratorTest extends TestCase {
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class IdGeneratorTest {
+  @Test
   public void test1234() {
     final HashSet<Integer> seen = new HashSet<Integer>();
     for (int i = 0; i < 1 << 16; i++) {
@@ -32,6 +36,7 @@
     assertEquals(0x0b966b11, IdGenerator.unmix(IdGenerator.mix(0x0b966b11)));
   }
 
+  @Test
   public void testFormat() {
     assertEquals("0000000f", IdGenerator.format(0xf));
     assertEquals("801234ab", IdGenerator.format(0x801234ab));
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/util/SocketUtilTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/util/SocketUtilTest.java
index 9e66046..5ce8882 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/util/SocketUtilTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/util/SocketUtilTest.java
@@ -20,8 +20,12 @@
 import static com.google.gerrit.server.util.SocketUtil.resolve;
 import static java.net.InetAddress.getByName;
 import static java.net.InetSocketAddress.createUnresolved;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
 import java.net.Inet4Address;
 import java.net.Inet6Address;
@@ -29,7 +33,8 @@
 import java.net.InetSocketAddress;
 import java.net.UnknownHostException;
 
-public class SocketUtilTest extends TestCase {
+public class SocketUtilTest {
+  @Test
   public void testIsIPv6() throws UnknownHostException {
     final InetAddress ipv6 = getByName("1:2:3:4:5:6:7:8");
     assertTrue(ipv6 instanceof Inet6Address);
@@ -40,12 +45,14 @@
     assertFalse(isIPv6(ipv4));
   }
 
+  @Test
   public void testHostname() {
     assertEquals("*", hostname(new InetSocketAddress(80)));
     assertEquals("localhost", hostname(new InetSocketAddress("localhost", 80)));
     assertEquals("foo", hostname(createUnresolved("foo", 80)));
   }
 
+  @Test
   public void testFormat() throws UnknownHostException {
     assertEquals("*:1234", SocketUtil.format(new InetSocketAddress(1234), 80));
     assertEquals("*", SocketUtil.format(new InetSocketAddress(80), 80));
@@ -64,6 +71,7 @@
         SocketUtil. format(new InetSocketAddress("localhost", 80), 80));
   }
 
+  @Test
   public void testParse() {
     assertEquals(new InetSocketAddress(1234), parse("*:1234", 80));
     assertEquals(new InetSocketAddress(80), parse("*", 80));
@@ -100,6 +108,7 @@
     }
   }
 
+  @Test
   public void testResolve() throws UnknownHostException {
     assertEquals(new InetSocketAddress(1234), resolve("*:1234", 80));
     assertEquals(new InetSocketAddress(80), resolve("*", 80));
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/util/SubmoduleSectionParserTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/util/SubmoduleSectionParserTest.java
index 299a245..ed86031 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/util/SubmoduleSectionParserTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/util/SubmoduleSectionParserTest.java
@@ -200,8 +200,9 @@
     expect(bbc.getSubsections("submodule"))
         .andReturn(sectionsToReturn.keySet());
 
-    for (final String id : sectionsToReturn.keySet()) {
-      final SubmoduleSection section = sectionsToReturn.get(id);
+    for (Map.Entry<String, SubmoduleSection> entry : sectionsToReturn.entrySet()) {
+      String id = entry.getKey();
+      final SubmoduleSection section = entry.getValue();
       expect(bbc.getString("submodule", id, "url")).andReturn(section.getUrl());
       expect(bbc.getString("submodule", id, "path")).andReturn(
           section.getPath());
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java
index a1e68d0..189f803 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.testutil;
 
+import static org.junit.Assert.assertEquals;
+
 import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
 import com.google.gerrit.reviewdb.client.SystemConfig;
 import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -26,8 +28,6 @@
 import com.google.inject.Guice;
 import com.google.inject.Inject;
 
-import junit.framework.TestCase;
-
 import org.eclipse.jgit.errors.ConfigInvalidException;
 
 import java.io.IOException;
@@ -162,6 +162,6 @@
 
   public void assertSchemaVersion() throws OrmException {
     final CurrentSchemaVersion act = getSchemaVersion();
-    TestCase.assertEquals(schemaVersion.getVersionNbr(), act.versionNbr);
+    assertEquals(schemaVersion.getVersionNbr(), act.versionNbr);
   }
 }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandModule.java
index c64f9d8..12f0db0 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandModule.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandModule.java
@@ -16,13 +16,13 @@
 
 import com.google.common.base.Objects;
 import com.google.common.base.Strings;
-import com.google.inject.AbstractModule;
+import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.inject.binder.LinkedBindingBuilder;
 
 import org.apache.sshd.server.Command;
 
 /** Module to register commands in the SSH daemon. */
-public abstract class CommandModule extends AbstractModule {
+public abstract class CommandModule extends LifecycleModule {
   /**
    * Configure a command to be invoked by name.
    *
@@ -72,7 +72,7 @@
    */
   protected void command(final CommandName parent,
       final Class<? extends BaseCommand> clazz) {
-    CommandMetaData meta = (CommandMetaData)clazz.getAnnotation(CommandMetaData.class);
+    CommandMetaData meta = clazz.getAnnotation(CommandMetaData.class);
     if (meta == null) {
       throw new IllegalStateException("no CommandMetaData annotation found");
     }
@@ -91,7 +91,7 @@
    */
   protected void alias(final CommandName parent, final String name,
       final Class<? extends BaseCommand> clazz) {
-    CommandMetaData meta = (CommandMetaData)clazz.getAnnotation(CommandMetaData.class);
+    CommandMetaData meta = clazz.getAnnotation(CommandMetaData.class);
     if (meta == null) {
       throw new IllegalStateException("no CommandMetaData annotation found");
     }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/NoShell.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/NoShell.java
index cde7ae8..78f006b 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/NoShell.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/NoShell.java
@@ -106,7 +106,7 @@
       } finally {
         sshScope.set(old);
       }
-      err.write(Constants.encode(message.toString()));
+      err.write(Constants.encode(message));
       err.flush();
 
       in.close();
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SingleCommandPluginModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SingleCommandPluginModule.java
new file mode 100644
index 0000000..0c5fca3
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SingleCommandPluginModule.java
@@ -0,0 +1,45 @@
+// 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.sshd;
+
+import com.google.common.base.Preconditions;
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.inject.binder.LinkedBindingBuilder;
+
+import org.apache.sshd.server.Command;
+
+import javax.inject.Inject;
+
+/**
+ * Binds one SSH command to the plugin name itself.
+ * <p>
+ * Cannot be combined with {@link PluginCommandModule}.
+ */
+public abstract class SingleCommandPluginModule extends CommandModule {
+  private CommandName command;
+
+  @Inject
+  void setPluginName(@PluginName String name) {
+    this.command = Commands.named(name);
+  }
+
+  @Override
+  protected final void configure() {
+    Preconditions.checkState(command != null, "@PluginName must be provided");
+    configure(bind(Commands.key(command)));
+  }
+
+  protected abstract void configure(LinkedBindingBuilder<Command> bind);
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
index 39b7f16..2322a3b 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.server.PeerDaemonUser;
 import com.google.gerrit.server.RemotePeer;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.config.GerritRequestModule;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.AsyncReceiveCommits;
@@ -47,7 +46,7 @@
 import java.util.Map;
 
 /** Configures standard dependencies for {@link SshDaemon}. */
-public class SshModule extends FactoryModule {
+public class SshModule extends LifecycleModule {
   private final Map<String, String> aliases;
 
   @Inject
@@ -87,25 +86,20 @@
 
     install(new DefaultCommandModule());
 
-    install(new LifecycleModule() {
-      @Override
-      protected void configure() {
-        bind(ModuleGenerator.class).to(SshAutoRegisterModuleGenerator.class);
-        bind(SshPluginStarterCallback.class);
-        bind(StartPluginListener.class)
-          .annotatedWith(UniqueAnnotations.create())
-          .to(SshPluginStarterCallback.class);
+    bind(ModuleGenerator.class).to(SshAutoRegisterModuleGenerator.class);
+    bind(SshPluginStarterCallback.class);
+    bind(StartPluginListener.class)
+      .annotatedWith(UniqueAnnotations.create())
+      .to(SshPluginStarterCallback.class);
 
-        bind(ReloadPluginListener.class)
-          .annotatedWith(UniqueAnnotations.create())
-          .to(SshPluginStarterCallback.class);
+    bind(ReloadPluginListener.class)
+      .annotatedWith(UniqueAnnotations.create())
+      .to(SshPluginStarterCallback.class);
 
-        listener().toInstance(registerInParentInjectors());
-        listener().to(SshLog.class);
-        listener().to(SshDaemon.class);
-        listener().to(CommandFactoryProvider.class);
-      }
-    });
+    listener().toInstance(registerInParentInjectors());
+    listener().to(SshLog.class);
+    listener().to(SshDaemon.class);
+    listener().to(CommandFactoryProvider.class);
   }
 
   private void configureAliases() {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshRemotePeerProvider.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshRemotePeerProvider.java
index 29ede85..2c77360 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshRemotePeerProvider.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshRemotePeerProvider.java
@@ -21,7 +21,7 @@
 import java.net.SocketAddress;
 
 @Singleton
-class SshRemotePeerProvider implements Provider<SocketAddress> {
+public class SshRemotePeerProvider implements Provider<SocketAddress> {
   private final Provider<SshSession> session;
 
   @Inject
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 440f236..9067b9b 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
@@ -35,7 +35,7 @@
 import java.util.Map;
 
 /** Guice scopes for state during an SSH connection. */
-class SshScope {
+public class SshScope {
   private static final Key<RequestCleanup> RC_KEY =
       Key.get(RequestCleanup.class);
 
@@ -119,7 +119,7 @@
     }
   }
 
-  static class SshSessionProvider implements Provider<SshSession> {
+  public static class SshSessionProvider implements Provider<SshSession> {
     @Override
     public SshSession get() {
       return requireContext().getSession();
@@ -181,7 +181,7 @@
   }
 
   /** Returns exactly one instance per command executed. */
-  static final Scope REQUEST = new Scope() {
+  public static final Scope REQUEST = new Scope() {
     public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
       return new Provider<T>() {
         public T get() {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ApproveOption.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ApproveOption.java
index 29250d3..fea16cd 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ApproveOption.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ApproveOption.java
@@ -24,8 +24,10 @@
 import org.kohsuke.args4j.spi.OneArgumentOptionHandler;
 import org.kohsuke.args4j.spi.OptionHandler;
 import org.kohsuke.args4j.spi.Setter;
+import org.kohsuke.args4j.spi.FieldSetter;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
 
 final class ApproveOption implements Option, Setter<Short> {
   private final String name;
@@ -46,6 +48,16 @@
   }
 
   @Override
+  public String[] depends() {
+    return new String[] {};
+  }
+
+  @Override
+  public boolean hidden() {
+    return false;
+  }
+
+  @Override
   public Class<? extends OptionHandler<Short>> handler() {
     return Handler.class;
   }
@@ -56,11 +68,6 @@
   }
 
   @Override
-  public boolean multiValued() {
-    return false;
-  }
-
-  @Override
   public String name() {
     return name;
   }
@@ -85,6 +92,16 @@
   }
 
   @Override
+  public FieldSetter asFieldSetter() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public AnnotatedElement asAnnotatedElement() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
   public void addValue(final Short val) {
     this.value = val;
   }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AproposCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AproposCommand.java
new file mode 100644
index 0000000..813f132
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AproposCommand.java
@@ -0,0 +1,46 @@
+// 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.sshd.commands;
+
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.documentation.QueryDocumentationExecutor;
+import com.google.gerrit.server.documentation.QueryDocumentationExecutor.DocResult;
+import com.google.gerrit.sshd.CommandMetaData;
+import com.google.gerrit.sshd.SshCommand;
+import com.google.inject.Inject;
+
+import org.kohsuke.args4j.Argument;
+
+import java.util.List;
+
+@CommandMetaData(name = "apropos", description = "Search in Gerrit documentation")
+final class AproposCommand extends SshCommand {
+  @Inject
+  private QueryDocumentationExecutor searcher;
+  @Inject
+  @CanonicalWebUrl String url;
+
+  @Argument(index=0, required = true, metaVar = "QUERY")
+  private String q;
+
+  @Override
+  public void run() throws Exception {
+    List<QueryDocumentationExecutor.DocResult> res = searcher.doQuery(q);
+    for (DocResult docResult : res) {
+      stdout.println(String.format("%s:\n%s%s\n", docResult.title, url,
+          docResult.url));
+    }
+  }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
index 521103b..75743b0 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
@@ -14,7 +14,6 @@
 
 package com.google.gerrit.sshd.commands;
 
-import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.sshd.CommandModule;
 import com.google.gerrit.sshd.CommandName;
 import com.google.gerrit.sshd.Commands;
@@ -36,6 +35,7 @@
     // SlaveCommandModule.
 
     command(gerrit).toProvider(new DispatchCommandProvider(gerrit));
+    command(gerrit, AproposCommand.class);
     command(gerrit, BanCommitCommand.class);
     command(gerrit, FlushCaches.class);
     command(gerrit, ListProjectsCommand.class);
@@ -76,11 +76,6 @@
 
     command("suexec").to(SuExec.class);
 
-    install(new LifecycleModule() {
-      @Override
-      protected void configure() {
-        listener().to(ShowCaches.StartupListener.class);
-      }
-    });
+    listener().to(ShowCaches.StartupListener.class);
   }
 }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
index 346bea7..81af0d8 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
@@ -43,6 +43,9 @@
   @Option(name = "--all", usage = "runs the Git garbage collection for all projects")
   private boolean all;
 
+  @Option(name = "--show-progress", usage = "progress information is shown")
+  private boolean showProgress;
+
   @Argument(index = 0, required = false, multiValued = true, metaVar = "NAME",
       usage = "projects for which the Git garbage collection should be run")
   private List<ProjectControl> projects = new ArrayList<ProjectControl>();
@@ -95,7 +98,7 @@
     }
 
     GarbageCollectionResult result =
-        garbageCollectionFactory.create().run(projectNames, stdout);
+        garbageCollectionFactory.create().run(projectNames, showProgress ? stdout : null);
     if (result.hasErrors()) {
       for (GarbageCollectionResult.Error e : result.getErrors()) {
         String msg;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
index 185bb67..af42e1b 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
@@ -72,6 +72,11 @@
     processor.setIncludeDependencies(on);
   }
 
+  @Option(name = "--all-reviewers", usage = "Include all reviewers")
+  void setAllReviewers(boolean on) {
+    processor.setIncludeAllReviewers(on);
+  }
+
   @Option(name = "--submit-records", usage = "Include submit and label status")
   void setSubmitRecords(boolean on) {
     processor.setIncludeSubmitRecords(on);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/QueryShell.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/QueryShell.java
index 5649843..69763d6 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/QueryShell.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/QueryShell.java
@@ -51,7 +51,7 @@
   }
 
   public static enum OutputFormat {
-    PRETTY, JSON, JSON_SINGLE;
+    PRETTY, JSON, JSON_SINGLE
   }
 
   private final BufferedReader in;
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 31f9301..2a8650a 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
@@ -62,12 +62,12 @@
   private final Set<Account.Id> reviewerId = new HashSet<Account.Id>();
   private final Set<Account.Id> ccId = new HashSet<Account.Id>();
 
-  @Option(name = "--reviewer", aliases = {"--re"}, multiValued = true, metaVar = "EMAIL", usage = "request reviewer for change(s)")
+  @Option(name = "--reviewer", aliases = {"--re"}, metaVar = "EMAIL", usage = "request reviewer for change(s)")
   void addReviewer(final Account.Id id) {
     reviewerId.add(id);
   }
 
-  @Option(name = "--cc", aliases = {}, multiValued = true, metaVar = "EMAIL", usage = "CC user on change(s)")
+  @Option(name = "--cc", aliases = {}, metaVar = "EMAIL", usage = "CC user on change(s)")
   void addCC(final Account.Id id) {
     ccId.add(id);
   }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index e5eb567..6665a25 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -22,23 +22,20 @@
 import com.google.gerrit.common.data.LabelValue;
 import com.google.gerrit.common.data.ReviewResult;
 import com.google.gerrit.common.data.ReviewResult.Error.Type;
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.changes.AbandonInput;
+import com.google.gerrit.extensions.api.changes.RestoreInput;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.RevId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.change.Abandon;
-import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.DeleteDraftPatchSet;
-import com.google.gerrit.server.change.PostReview;
-import com.google.gerrit.server.change.Restore;
-import com.google.gerrit.server.change.RevisionResource;
-import com.google.gerrit.server.change.Submit;
 import com.google.gerrit.server.changedetail.PublishDraft;
 import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.InvalidChangeOperationException;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.NoSuchProjectException;
@@ -131,32 +128,17 @@
   private ReviewDb db;
 
   @Inject
-  private DeleteDraftPatchSet deleteDraftPatchSetImpl;
-
-  @Inject
   private ProjectControl.Factory projectControlFactory;
 
   @Inject
   private AllProjectsName allProjects;
 
   @Inject
-  private ChangeControl.Factory changeControlFactory;
-
-  @Inject
-  private Provider<Abandon> abandonProvider;
-
-  @Inject
-  private Provider<PostReview> reviewProvider;
+  private Provider<GerritApi> gApi;
 
   @Inject
   private PublishDraft.Factory publishDraftFactory;
 
-  @Inject
-  private Provider<Restore> restoreProvider;
-
-  @Inject
-  private Provider<Submit> submitProvider;
-
   private List<ApproveOption> optionList;
   private Map<String, Short> customLabels;
 
@@ -212,10 +194,12 @@
     }
   }
 
-  private void applyReview(final ChangeControl ctl, final PatchSet patchSet,
-      final PostReview.Input review) throws Exception {
-    reviewProvider.get().apply(new RevisionResource(
-        new ChangeResource(ctl), patchSet), review);
+  private void applyReview(PatchSet patchSet,
+      final ReviewInput review) throws Exception {
+    gApi.get().changes()
+        .id(patchSet.getId().getParentKey().get())
+        .revision(patchSet.getRevision().get())
+        .review(review);
   }
 
   private void approveOne(final PatchSet patchSet) throws Exception {
@@ -224,10 +208,10 @@
       changeComment = "";
     }
 
-    PostReview.Input review = new PostReview.Input();
+    ReviewInput review = new ReviewInput();
     review.message = Strings.emptyToNull(changeComment);
     review.labels = Maps.newTreeMap();
-    review.drafts = PostReview.DraftHandling.PUBLISH;
+    review.drafts = ReviewInput.DraftHandling.PUBLISH;
     review.strictLabels = false;
     for (ApproveOption ao : optionList) {
       Short v = ao.value();
@@ -245,44 +229,41 @@
     }
 
     try {
-      ChangeControl ctl =
-          changeControlFactory.controlFor(patchSet.getId().getParentKey());
-
       if (abandonChange) {
-        final Abandon abandon = abandonProvider.get();
-        final Abandon.Input input = new Abandon.Input();
+        AbandonInput input = new AbandonInput();
         input.message = changeComment;
-        applyReview(ctl, patchSet, review);
+        applyReview(patchSet, review);
         try {
-          abandon.apply(new ChangeResource(ctl), input);
+          gApi.get().changes()
+              .id(patchSet.getId().getParentKey().get())
+              .abandon(input);
         } catch (AuthException e) {
           writeError("error: " + parseError(Type.ABANDON_NOT_PERMITTED) + "\n");
         } catch (ResourceConflictException e) {
           writeError("error: " + parseError(Type.CHANGE_IS_CLOSED) + "\n");
         }
       } else if (restoreChange) {
-        final Restore restore = restoreProvider.get();
-        final Restore.Input input = new Restore.Input();
+        RestoreInput input = new RestoreInput();
         input.message = changeComment;
         try {
-          restore.apply(new ChangeResource(ctl), input);
-          applyReview(ctl, patchSet, review);
+          gApi.get().changes()
+              .id(patchSet.getId().getParentKey().get())
+              .restore(input);
+          applyReview(patchSet, review);
         } catch (AuthException e) {
           writeError("error: " + parseError(Type.RESTORE_NOT_PERMITTED) + "\n");
         } catch (ResourceConflictException e) {
           writeError("error: " + parseError(Type.CHANGE_NOT_ABANDONED) + "\n");
         }
       } else {
-        applyReview(ctl, patchSet, review);
+        applyReview(patchSet, review);
       }
 
       if (submitChange) {
-        Submit submit = submitProvider.get();
-        Submit.Input input = new Submit.Input();
-        input.waitForMerge = true;
-        submit.apply(new RevisionResource(
-            new ChangeResource(ctl), patchSet),
-          input);
+        gApi.get().changes()
+            .id(patchSet.getId().getParentKey().get())
+            .revision(patchSet.getRevision().get())
+            .submit();
       }
 
       if (publishPatchSet) {
@@ -290,9 +271,10 @@
             publishDraftFactory.create(patchSet.getId()).call();
         handleReviewResultErrors(result);
       } else if (deleteDraftPatchSet) {
-        deleteDraftPatchSetImpl.apply(new RevisionResource(
-            new ChangeResource(ctl), patchSet),
-            new DeleteDraftPatchSet.Input());
+        gApi.get().changes()
+            .id(patchSet.getId().getParentKey().get())
+            .revision(patchSet.getRevision().get())
+            .delete();
       }
     } catch (InvalidChangeOperationException e) {
       throw error(e.getMessage());
@@ -304,6 +286,8 @@
       throw error(e.getMessage());
     } catch (ResourceConflictException e) {
       throw error(e.getMessage());
+    } catch (RestApiException e) {
+      throw error(e.getMessage());
     }
   }
 
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index 5736bb6..f634147 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -70,16 +70,16 @@
   @Option(name = "--inactive", usage = "set account's state to inactive")
   private boolean inactive;
 
-  @Option(name = "--add-email", multiValued = true, metaVar = "EMAIL", usage = "email addresses to add to the account")
+  @Option(name = "--add-email", metaVar = "EMAIL", usage = "email addresses to add to the account")
   private List<String> addEmails = new ArrayList<String>();
 
-  @Option(name = "--delete-email", multiValued = true, metaVar = "EMAIL", usage = "email addresses to delete from the account")
+  @Option(name = "--delete-email", metaVar = "EMAIL", usage = "email addresses to delete from the account")
   private List<String> deleteEmails = new ArrayList<String>();
 
-  @Option(name = "--add-ssh-key", multiValued = true, metaVar = "-|KEY", usage = "public keys to add to the account")
+  @Option(name = "--add-ssh-key", metaVar = "-|KEY", usage = "public keys to add to the account")
   private List<String> addSshKeys = new ArrayList<String>();
 
-  @Option(name = "--delete-ssh-key", multiValued = true, metaVar = "-|KEY", usage = "public keys to delete from the account")
+  @Option(name = "--delete-ssh-key", metaVar = "-|KEY", usage = "public keys to delete from the account")
   private List<String> deleteSshKeys = new ArrayList<String>();
 
   @Option(name = "--http-password", metaVar = "PASSWORD", usage = "password for HTTP authentication for the account")
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
index 6dc79ff..ce015b6 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
@@ -118,7 +118,7 @@
     DeleteReviewer delete = deleteReviewerProvider.get();
     for (Account.Id reviewer : toRemove) {
       ReviewerResource rsrc = reviewerFactory.create(changeRsrc, reviewer);
-      String error = null;;
+      String error = null;
       try {
         delete.apply(rsrc, new DeleteReviewer.Input());
       } catch (ResourceNotFoundException e) {
@@ -142,8 +142,6 @@
       String error;
       try {
         error = post.apply(changeRsrc, input).error;
-      } catch (ResourceNotFoundException e) {
-        error = String.format("could not add %s: not found", reviewer);
       } catch (Exception e) {
         error = String.format("could not add %s: %s", reviewer, e.getMessage());
       }
diff --git a/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java
index b75635f..7fc6d6f 100644
--- a/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java
+++ b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java
@@ -52,10 +52,13 @@
 import org.kohsuke.args4j.spi.EnumOptionHandler;
 import org.kohsuke.args4j.spi.OptionHandler;
 import org.kohsuke.args4j.spi.Setter;
+import org.kohsuke.args4j.spi.FieldSetter;
+
 
 import java.io.StringWriter;
 import java.io.Writer;
 import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -381,6 +384,16 @@
     }
 
     @Override
+    public String[] depends() {
+      return new String[] {};
+    }
+
+    @Override
+    public boolean hidden() {
+      return false;
+    }
+
+    @Override
     public String usage() {
       return "display this help text";
     }
@@ -401,11 +414,6 @@
     }
 
     @Override
-    public boolean multiValued() {
-      return false;
-    }
-
-    @Override
     public boolean required() {
       return false;
     }
@@ -416,13 +424,23 @@
     }
 
     @Override
+    public FieldSetter asFieldSetter() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public AnnotatedElement asAnnotatedElement() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
     public Class<Boolean> getType() {
       return Boolean.class;
     }
 
     @Override
     public boolean isMultiValued() {
-      return multiValued();
+      return false;
     }
   }
 }
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 5936911..36595c0 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
@@ -196,16 +196,10 @@
       final DataSourceType dst = Guice.createInjector(new DataSourceModule(),
           configModule, sitePathModule).getInstance(
             Key.get(DataSourceType.class, Names.named(dbType.toLowerCase())));
-      modules.add(new AbstractModule() {
-        @Override
-        protected void configure() {
-          bind(DataSourceType.class).toInstance(dst);
-        }
-      });
-
       modules.add(new LifecycleModule() {
         @Override
         protected void configure() {
+          bind(DataSourceType.class).toInstance(dst);
           bind(DataSourceProvider.Context.class).toInstance(
               DataSourceProvider.Context.MULTI_USER);
           bind(Key.get(DataSource.class, Names.named("ReviewDb"))).toProvider(
diff --git a/lib/BUCK b/lib/BUCK
index c9549b3..2469692 100644
--- a/lib/BUCK
+++ b/lib/BUCK
@@ -121,8 +121,8 @@
 
 maven_jar(
   name = 'args4j',
-  id = 'args4j:args4j:2.0.16',
-  sha1 = '9f00fb12820743b9e05c686eba543d64dd43f2b1',
+  id = 'args4j:args4j:2.0.26',
+  sha1 = '01ebb18ebb3b379a74207d5af4ea7c8338ebd78b',
   license = 'args4j',
 )
 
@@ -223,8 +223,8 @@
 
 maven_jar(
   name = 'easymock',
-  id = 'org.easymock:easymock:3.1',
-  sha1 = '3e127311a86fc2e8f550ef8ee4abe094bbcf7e7e',
+  id = 'org.easymock:easymock:3.2',
+  sha1 = '00c82f7fa3ef377d8954b1db25123944b5af2ba4',
   license = 'DO_NOT_DISTRIBUTE',
   deps = [
     ':cglib-2_2',
diff --git a/lib/asciidoctor/BUCK b/lib/asciidoctor/BUCK
index 0b07b69..b1d5933 100644
--- a/lib/asciidoctor/BUCK
+++ b/lib/asciidoctor/BUCK
@@ -31,6 +31,7 @@
   srcs = ['java/DocIndexer.java'],
   deps = [
     ':asciidoc_lib',
+    '//gerrit-server:constants',
     '//lib:args4j',
     '//lib:guava',
     '//lib/lucene:analyzers-common',
diff --git a/lib/asciidoctor/java/DocIndexer.java b/lib/asciidoctor/java/DocIndexer.java
index 497cba5..96b1449 100644
--- a/lib/asciidoctor/java/DocIndexer.java
+++ b/lib/asciidoctor/java/DocIndexer.java
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 import com.google.common.io.Files;
+import com.google.gerrit.server.documentation.Constants;
 
 import org.apache.lucene.analysis.standard.StandardAnalyzer;
 import org.apache.lucene.analysis.util.CharArraySet;
@@ -43,9 +44,6 @@
 
 public class DocIndexer {
   private static final Version LUCENE_VERSION = Version.LUCENE_44;
-  private static final String DOC_FIELD = "doc";
-  private static final String URL_FIELD = "url";
-  private static final String TITLE_FIELD = "title";
 
   @Option(name = "-z", usage = "output zip file")
   private String zipFile;
@@ -100,10 +98,10 @@
           inputFile, inExt, outExt);
       FileReader reader = new FileReader(file);
       Document doc = new Document();
-      doc.add(new TextField(DOC_FIELD, reader));
+      doc.add(new TextField(Constants.DOC_FIELD, reader));
       doc.add(new StringField(
-            URL_FIELD, prefix + outputFile, Field.Store.YES));
-      doc.add(new TextField(TITLE_FIELD, title, Field.Store.YES));
+            Constants.URL_FIELD, prefix + outputFile, Field.Store.YES));
+      doc.add(new TextField(Constants.TITLE_FIELD, title, Field.Store.YES));
       iwriter.addDocument(doc);
       reader.close();
     }
diff --git a/lib/jetty/BUCK b/lib/jetty/BUCK
index 6eac1a9..6314f99 100644
--- a/lib/jetty/BUCK
+++ b/lib/jetty/BUCK
@@ -1,12 +1,12 @@
 include_defs('//lib/maven.defs')
 
-VERSION = '8.1.7.v20120910'
+VERSION = '9.0.6.v20130930'
 EXCLUDE = ['about.html']
 
 maven_jar(
   name = 'servlet',
   id = 'org.eclipse.jetty:jetty-servlet:' + VERSION,
-  sha1 = '93da01e3ea26e70449e9a1a0affa5c31436be5a0',
+  sha1 = 'b6953af6f857e78fe3c46935fa96f9c80a9cbefd',
   license = 'Apache2.0',
   deps = [
     ':security',
@@ -18,7 +18,7 @@
 maven_jar(
   name = 'security',
   id = 'org.eclipse.jetty:jetty-security:' + VERSION,
-  sha1 = '8d78beb7a07f4cccee05a3f16a264f1025946258',
+  sha1 = 'b16cd29ad9d3d3cca8176a654adf4b2ddca0eb3e',
   license = 'Apache2.0',
   deps = [':server'],
   exclude = EXCLUDE,
@@ -28,7 +28,7 @@
 maven_jar(
   name = 'server',
   id = 'org.eclipse.jetty:jetty-server:' + VERSION,
-  sha1 = '6c81f733f28713919e99c2f8952e6ca5178033cd',
+  sha1 = 'd89016c2bcd380f548530fa049295e62601dc564',
   license = 'Apache2.0',
   deps = [
     ':continuation',
@@ -41,7 +41,7 @@
 maven_jar(
   name = 'continuation',
   id = 'org.eclipse.jetty:jetty-continuation:' + VERSION,
-  sha1 = 'f60cfe6267038000b459508529c88737601081e4',
+  sha1 = '4f942a52a3e996634ff302ab98f46d224b0db9d2',
   license = 'Apache2.0',
   exclude = EXCLUDE,
 )
@@ -49,7 +49,7 @@
 maven_jar(
   name = 'http',
   id = 'org.eclipse.jetty:jetty-http:' + VERSION,
-  sha1 = '10126433876cd74534695f7f99c4362596555493',
+  sha1 = 'b3a2302717ac1889b4a17ed03e2555f8291121b9',
   license = 'Apache2.0',
   deps = [':io'],
   exclude = EXCLUDE,
@@ -58,7 +58,7 @@
 maven_jar(
   name = 'io',
   id = 'org.eclipse.jetty:jetty-io:' + VERSION,
-  sha1 = 'a81f746ae1b10c37e1bb0a01d1374c202c0bd549',
+  sha1 = 'f3a66e0507d963c51e280243f0472a5b2eadc8b1',
   license = 'Apache2.0',
   deps = [':util'],
   exclude = EXCLUDE,
@@ -68,7 +68,7 @@
 maven_jar(
   name = 'util',
   id = 'org.eclipse.jetty:jetty-util:' + VERSION,
-  sha1 = '7eb2004ab2c22fd3b00095bd9ba0f32a9e88f6a5',
+  sha1 = 'f36c9e61559d1154be9b52803ef4f586e401dac6',
   license = 'Apache2.0',
   exclude = EXCLUDE,
   visibility = [],
diff --git a/lib/jgit/BUCK b/lib/jgit/BUCK
index 3e481bf..bc3044f 100644
--- a/lib/jgit/BUCK
+++ b/lib/jgit/BUCK
@@ -26,6 +26,7 @@
   license = 'jgit',
   repository = REPO,
   deps = [':jgit'],
+  unsign = True,
   exclude = [
     'about.html',
     'plugin.properties',
@@ -38,6 +39,7 @@
   sha1 = 'a8b47bb41cec25b1d128f7d267badbc7dcf6d9aa',
   license = 'DO_NOT_DISTRIBUTE',
   repository = REPO,
+  unsign = True,
   deps = [':jgit'],
 )
 
diff --git a/lib/lucene/BUCK b/lib/lucene/BUCK
index 38ece4c..5973473 100644
--- a/lib/lucene/BUCK
+++ b/lib/lucene/BUCK
@@ -41,6 +41,14 @@
 )
 
 maven_jar(
+  name = 'query-parser',
+  id = 'org.apache.lucene:lucene-queryparser:4.4.0',
+  bin_sha1 = 'e2fca26d9c64f3aad7b8a3461dbab14782107a06',
+  src_sha1 = 'f23e42ab90b5b7eb888d394282eba65362e88606',
+  license = 'Apache2.0',
+)
+
+maven_jar(
   name = 'spellchecker',
   id = 'org.apache.lucene:lucene-spellchecker:3.6.2',
   bin_sha1 = '15db0c0cfee44e275f15ad046e46b9a05910ad24',
diff --git a/plugins/commit-message-length-validator b/plugins/commit-message-length-validator
index 45f347d..c173419 160000
--- a/plugins/commit-message-length-validator
+++ b/plugins/commit-message-length-validator
@@ -1 +1 @@
-Subproject commit 45f347d0e258ef7b871b046bfa96b9f902063b10
+Subproject commit c1734194c6a47492b1a5462206f59b585155406e
diff --git a/plugins/cookbook-plugin b/plugins/cookbook-plugin
index 5ee3f28..1b1aca1 160000
--- a/plugins/cookbook-plugin
+++ b/plugins/cookbook-plugin
@@ -1 +1 @@
-Subproject commit 5ee3f28739700e97b91231a2b694f3ba78065e86
+Subproject commit 1b1aca1dd61c033840bc465fa8646c22f5467f3a
diff --git a/plugins/download-commands b/plugins/download-commands
index 05975dd..fe2bc6b 160000
--- a/plugins/download-commands
+++ b/plugins/download-commands
@@ -1 +1 @@
-Subproject commit 05975dd6c8ca44de2e01cede16a94df49a3a825f
+Subproject commit fe2bc6be5ef964a5df247a85f82c0155dc2f8876
diff --git a/plugins/replication b/plugins/replication
index fa66d17..5ee79cc 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit fa66d17b1d6ed3266d0e9061a852fc530ec2ea73
+Subproject commit 5ee79cc5355e24bee7d38f697aa834e1bd6e0e7d
diff --git a/plugins/reviewnotes b/plugins/reviewnotes
index 9905f7a..c9fc694 160000
--- a/plugins/reviewnotes
+++ b/plugins/reviewnotes
@@ -1 +1 @@
-Subproject commit 9905f7af6c4e258365413a03f092898b167ea25a
+Subproject commit c9fc694ebd17d8331710c5193e729cee70fab325
diff --git a/plugins/singleusergroup b/plugins/singleusergroup
new file mode 160000
index 0000000..c9ad9d1
--- /dev/null
+++ b/plugins/singleusergroup
@@ -0,0 +1 @@
+Subproject commit c9ad9d1be42d5139fc1c447df644866f1d23ed15
diff --git a/tools/build.defs b/tools/build.defs
index b62c850..4bb48ea 100644
--- a/tools/build.defs
+++ b/tools/build.defs
@@ -14,7 +14,12 @@
 
 # These definitions support building a runnable version of Gerrit.
 
-DOCS = ['//Documentation:html.zip']
+DOCS_SRC = genfile('Documentation/html.zip')
+DOCS_LIB = '//Documentation:index_lib'
+DOCS_DEP = [
+  '//Documentation:html',
+  '//Documentation:index_lib',
+]
 LIBS = [
   '//gerrit-war:log4j-config',
   '//gerrit-war:init',
@@ -36,7 +41,8 @@
     libs = [],
     pgmlibs = [],
     context = [],
-    visibility = []
+    visibility = [],
+    docs = False
     ):
   cmd = ['$(exe //tools:pack_war)', '-o', '$OUT', '--tmp', '$TMP']
   for l in libs:
@@ -46,6 +52,10 @@
 
   src = []
   dep = []
+  if docs:
+    src.append(DOCS_SRC)
+    dep.extend(DOCS_DEP)
+    cmd.extend(['--lib', DOCS_LIB])
   if context:
     root = get_base_path()
     if root:
@@ -56,6 +66,7 @@
         r = root + r[2:]
       r = r.replace(':', '/')
       src.append(genfile(r))
+  if src:
     cmd.append('$SRCS')
 
   genrule(
@@ -67,7 +78,7 @@
     visibility = visibility,
   )
 
-def gerrit_war(name, ui = 'ui_optdbg', context = []):
+def gerrit_war(name, ui = 'ui_optdbg', context = [], docs = False):
   war(
     name = name,
     libs = LIBS + ['//gerrit-war:version'],
@@ -77,4 +88,5 @@
       '//gerrit-war:webapp_assets.zip',
       '//gerrit-gwtui:' + ui + '.zip',
     ] + context,
+    docs = docs,
   )
diff --git a/tools/default.defs b/tools/default.defs
index 4ed5635..b7ef6b4 100644
--- a/tools/default.defs
+++ b/tools/default.defs
@@ -14,6 +14,8 @@
 
 # Rule definitions loaded by default into every BUCK file.
 
+include_defs('//tools/gwt-constants.defs')
+
 def genantlr(
     name,
     srcs,
@@ -128,6 +130,7 @@
     deps = [],
     srcs = [],
     resources = [],
+    gwt_module = None,
     manifest_file = None,
     manifest_entries = [],
     type = 'plugin',
@@ -149,20 +152,47 @@
     srcs = mf_src,
     out = 'MANIFEST.MF',
   )
+  gwt_deps = []
+  static_jars = []
+  if gwt_module:
+    gwt_deps = GWT_PLUGIN_DEPS
+    static_jars = [':%s-static-jar' % name]
   java_library2(
     name = name + '__plugin',
     srcs = srcs,
     resources = resources,
     deps = deps,
-    compile_deps = ['//:%s-lib' % type],
+    compile_deps = ['//:%s-lib' % type] + gwt_deps,
   )
+  if gwt_module:
+    prebuilt_jar(
+      name = '%s-static-jar' % name,
+      binary_jar = genfile('%s-static.zip' % name),
+      deps = [':%s-static' % name],
+    )
+    genrule(
+      name = '%s-static' % name,
+      cmd = 'mkdir -p $TMP/static' +
+        ';unzip -qd $TMP/static $(location %s)' %
+        ':%s__gwt_application' % name +
+        ';cd $TMP' +
+        ';zip -qr $OUT .',
+      out = '%s-static.zip' % name,
+      deps = [':%s__gwt_application' % name]
+    )
+    gwt_application(
+      name = name + '__gwt_application',
+      module_target = gwt_module,
+      compiler_opts = GWT_COMPILER_OPTS,
+      deps = [':%s__plugin' % name] + gwt_deps,
+    )
   java_binary(
     name = name,
     manifest_file = genfile('MANIFEST.MF'),
     deps = [
       ':%s__plugin' % name,
       ':%s__manifest' % name,
-    ],
+    ] + static_jars,
     visibility = visibility,
   )
 
diff --git a/tools/eclipse/BUCK b/tools/eclipse/BUCK
index 9d6dd53..6f4e704 100644
--- a/tools/eclipse/BUCK
+++ b/tools/eclipse/BUCK
@@ -9,9 +9,12 @@
     '//gerrit-gwtui:ui_tests',
     '//gerrit-httpd:httpd_tests',
     '//gerrit-main:main_lib',
+    '//gerrit-patch-jgit:jgit_patch_tests',
+    '//gerrit-plugin-gwtui:client',
     '//gerrit-server:server__compile',
     '//lib/asciidoctor:asciidoc_lib',
     '//lib/asciidoctor:doc_indexer_lib',
     '//lib/prolog:compiler_lib',
+    '//Documentation:index_lib',
   ] + scan_plugins(),
 )
diff --git a/tools/gwt-constants.defs b/tools/gwt-constants.defs
new file mode 100644
index 0000000..56978e7
--- /dev/null
+++ b/tools/gwt-constants.defs
@@ -0,0 +1,13 @@
+GWT_COMPILER_OPTS = [
+  '-strict',
+  '-style', 'OBF',
+  '-optimize', '9',
+  '-XdisableClassMetadata',
+  '-XdisableCastChecking',
+  '-XenableClosureCompiler',
+]
+
+GWT_PLUGIN_DEPS = [
+  '//gerrit-plugin-gwtui:client',
+  '//lib/gwt:user',
+]
diff --git a/tools/maven/BUCK b/tools/maven/BUCK
index 0a470a4..50b403b 100644
--- a/tools/maven/BUCK
+++ b/tools/maven/BUCK
@@ -10,10 +10,12 @@
   jar = {
     'gerrit-extension-api': '//:extension-api',
     'gerrit-plugin-api': '//:plugin-api',
+    'gerrit-plugin-gwtui': '//:plugin-gwtui',
   },
   src = {
     'gerrit-extension-api': '//:extension-api-src',
     'gerrit-plugin-api': '//:plugin-api-src',
+    'gerrit-plugin-gwtui': '//:plugin-gwtui-src',
   },
 )