Merge 'v2.4.3'

* commit 'v2.4.3':
  Update JGit to patch security hole during clone

Change-Id: I5a236e06384b7a1ba4c923f3219c04b6922a395d
diff --git a/.gitignore b/.gitignore
index f318b65..465893d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,5 @@
 /.settings/org.eclipse.jdt.core.prefs
 /.settings/org.maven.ide.eclipse.prefs
 /test_site
+/.idea
+/gerrit-parent.iml
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 5522239..4c64dfe 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -76,6 +76,7 @@
 	@$(ASCIIDOC) -a toc \
 		-a data-uri \
 		-a 'revision=$(REVISION)' \
+		-a 'newline=\n' \
 		-b xhtml11 \
 		-f asciidoc.conf \
 		$(ASCIIDOC_EXTRA) \
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 8d89fa2..879d1ac 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -23,7 +23,7 @@
 
 Users in the 'Administrators' group can perform any action under
 the Admin menu, to any group or project, without further validation
-of any other access controls.  In most installations only those
+or any other access controls.  In most installations only those
 users who have direct filesystem and database access would be
 placed into this group.
 
@@ -444,6 +444,17 @@
 A restart is required after making database changes.
 See <<restart_changes,below>>.
 
+[[category_abandon]]
+Abandon
+~~~~~~~
+
+This category controls whether users are allowed to abandon changes
+to projects in Gerrit. It can give permission to abandon a specific
+change to a given ref.
+
+This also grants the permission to restore a change if the change
+can be uploaded.
+
 [[category_create]]
 Create reference
 ~~~~~~~~~~~~~~~~
@@ -451,7 +462,7 @@
 The create reference category controls whether it is possible to
 create new references, branches or tags.  This implies that the
 reference must not already exist, it's not a destructive permission
-in that you can't overwrite or remove any previosuly existing
+in that you can't overwrite or remove any previously existing
 references (and also discard any commits in the process).
 
 It's probably most common to either permit the creation of a single
@@ -462,7 +473,7 @@
 branch permissions, allowing the holder of both to create new branches
 as well as bypass review for new commits on that branch.
 
-To push lightweight (non annotated) tags, grant
+To push lightweight (non-annotated) tags, grant
 `Create Reference` for reference name `refs/tags/*`, as lightweight
 tags are implemented just like branches in Git.
 
@@ -622,13 +633,19 @@
 Push Merge Commits
 ~~~~~~~~~~~~~~~~~~~~
 
-The `Push Merge Commit` permits the user to upload merge commits.
-It's an addon to the <<category_push,Push>> access right, and so it
-won't be sufficient with only `Push Merge Commit` granted for a push
-to happen.  Some projects wish to restrict merges to being created by
-Gerrit. By granting `Push` without `Push Merge Commit`, the only
+The `Push Merge Commit` access right permits the user to upload merge
+commits.  It's an addon to the <<category_push,Push>> access right, and
+so it won't be sufficient with only `Push Merge Commit` granted for a
+push to happen.  Some projects wish to restrict merges to being created
+by Gerrit. By granting `Push` without `Push Merge Commit`, the only
 merges that enter the system will be those created by Gerrit.
 
+The reference name connected to a `Push Merge Commit` entry must always
+be prefixed with `refs/for/`, for example `refs/for/refs/heads/BRANCH`.
+This applies even for an entry that complements a `Push` entry for
+`refs/heads/BRANCH` that allows direct pushes of non-merge commits, and
+the intention of the `Push Merge Commit` entry is to allow direct pushes
+of merge commits.
 
 [[category_push_annotated]]
 Push Annotated Tag
@@ -809,7 +826,7 @@
 
 Below follows a set of typical roles on a server and which access
 rights these roles typically should be granted. You may see them as
-general guide lines for a typical way to set up your project on a
+general guidelines for a typical way to set up your project on a
 brand new Gerrit instance.
 
 [[examples_contributor]]
@@ -892,8 +909,8 @@
 project and how much the CI system can be trusted for accurate results, a
 blocking label might not be feasible.  A recommended alternative is to set the
 label `Code-review` to -1 instead, as it isn't a blocking label but still
-shows a red label in the Gerrit UI.  Optionally; to enable the possibility to
-deliver different results (build error vs unstable for instance) it's also
+shows a red label in the Gerrit UI.  Optionally, to enable the possibility to
+deliver different results (build error vs unstable for instance), it's also
 possible to set `Code-review` +1 as well.
 
 If pushing new changes is granted, it's possible to automate cherry-pick of
@@ -1064,6 +1081,15 @@
 either link:cmd-create-project.html[create new git projects via ssh]
 or via the web UI.
 
+[[capability_emailReviewers]]
+Email Reviewers
+~~~~~~~~~~~~~~~
+
+Allow or deny sending email to change reviewers and watchers.  This can be used
+to deny build bots from emailing reviewers and people who watch the change.
+Instead, only the authors of the change and those who starred it will be
+emailed.  The allow rules are evaluated before deny rules, however the default
+is to allow emailing, if no explicit rule is matched.
 
 [[capability_flushCaches]]
 Flush Caches
@@ -1136,7 +1162,8 @@
 Start Replication
 ~~~~~~~~~~~~~~~~~
 
-Allow access to execute link:cmd-replicate.html[the `gerrit replicate` command].
+Allow access to execute `replication start` command, if the
+replication plugin is installed on the server.
 
 
 [[capability_viewCaches]]
diff --git a/Documentation/cmd-ban-commit.txt b/Documentation/cmd-ban-commit.txt
new file mode 100644
index 0000000..fb4a2ac
--- /dev/null
+++ b/Documentation/cmd-ban-commit.txt
@@ -0,0 +1,60 @@
+gerrit ban-commit
+=================
+
+NAME
+----
+gerrit ban-commit - Bans a commit from a project's repository.
+
+SYNOPSIS
+--------
+[verse]
+'ssh' -p <port> <host> 'gerrit ban-commit'
+  [--reason <REASON>]
+  <PROJECT>
+  <COMMIT> ...
+
+DESCRIPTION
+-----------
+Marks a commit as banned for the specified repository.  If a commit is
+banned Gerrit rejects every push that includes this commit with
+link:error-contains-banned-commit.html[contains banned commit ...].
+
+[NOTE]
+This command just marks the commit as banned, but it does not remove
+the commit from the history of any central branch.  This needs to be
+done manually.
+
+ACCESS
+------
+Caller must be owner of the project or be a member of the privileged
+'Administrators' group.
+
+SCRIPTING
+---------
+This command is intended to be used in scripts.
+
+OPTIONS
+-------
+<PROJECT>::
+	Required; name of the project for which the commit should be
+	banned.
+
+<COMMIT>::
+	Required; commit(s) that should be banned.
+
+--reason::
+	Reason for banning the commit.
+
+EXAMPLES
+--------
+Ban commit `421919d015c062fd28901fe144a78a555d0b5984` from project
+`myproject`:
+
+====
+	$ ssh -p 29418 review.example.com gerrit ban-commit myproject \
+	421919d015c062fd28901fe144a78a555d0b5984
+====
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/cmd-cherry-pick.txt b/Documentation/cmd-cherry-pick.txt
index 568c872..d051a9a 100644
--- a/Documentation/cmd-cherry-pick.txt
+++ b/Documentation/cmd-cherry-pick.txt
@@ -39,7 +39,7 @@
 ====
   $ scp -p -P 29418 john.doe@review.example.com:bin/gerrit-cherry-pick ~/bin/
 
-  $ curl http://review.example.com/tools/bin/gerrit-cherry-pick
+  $ curl -o ~/bin/gerrit-cherry-pick http://review.example.com/tools/bin/gerrit-cherry-pick
 ====
 
 GERRIT
diff --git a/Documentation/cmd-create-account.txt b/Documentation/cmd-create-account.txt
index 98f950f..16b2eb5 100644
--- a/Documentation/cmd-create-account.txt
+++ b/Documentation/cmd-create-account.txt
@@ -13,6 +13,7 @@
   [--full-name <FULLNAME>]
   [--email <EMAIL>]
   [--ssh-key - | <KEY>]
+  [--http-password <PASSWORD>]
   <USERNAME>
 
 DESCRIPTION
@@ -59,6 +60,9 @@
 --email::
 	Preferred email address for the user account.
 
+--http-password::
+    HTTP password for the user account.
+
 EXAMPLES
 --------
 Create a new user account called `watcher`:
diff --git a/Documentation/cmd-create-group.txt b/Documentation/cmd-create-group.txt
index 475d2c5..8dc6dcc 100644
--- a/Documentation/cmd-create-group.txt
+++ b/Documentation/cmd-create-group.txt
@@ -9,8 +9,8 @@
 --------
 [verse]
 'ssh' -p <port> <host> 'gerrit create-group'
-  [--owner <GROUP>]
-  [--description <DESC>]
+  [--owner <GROUP> | -o <GROUP>]
+  [--description <DESC> | -d <DESC>]
   [--member <USERNAME>]
   [--group <GROUP>]
   [--visible-to-all]
@@ -53,6 +53,13 @@
 --member::
 	User name to become initial member of the group.  Multiple --member
 	options may be specified to add more initial members.
++
+Trying to add a user that doesn't have an account in Gerrit fails,
+unless LDAP is used for authentication. If LDAP is used for
+authentication and the user is not found, Gerrit tries to authenticate
+the user against the LDAP backend. If the authentication is successful
+a user account is automatically created, so that the user can be added
+to the group.
 
 --group::
 	Group name to include in the group.  Multiple --group options may
diff --git a/Documentation/cmd-create-project.txt b/Documentation/cmd-create-project.txt
index f22141c..d0e56fd 100644
--- a/Documentation/cmd-create-project.txt
+++ b/Documentation/cmd-create-project.txt
@@ -14,12 +14,12 @@
   [--suggest-parents | -S ]
   [--permissions-only]
   [--description <DESC> | -d <DESC>]
-  [--submit-type <TYPE> |  -t <TYPE>]
+  [--submit-type <TYPE> | -t <TYPE>]
   [--use-contributor-agreements | --ca]
   [--use-signed-off-by | --so]
   [--use-content-merge]
   [--require-change-id | --id]
-  [--branch <REF> | -b <REF>]
+  [[--branch <REF> | -b <REF>] ...]
   [--empty-commit]
   { <NAME> | --name <NAME> }
 
@@ -59,8 +59,11 @@
 
 --branch::
 -b::
-	Name of the initial branch in the newly created project.
-	Defaults to 'master'.
+	Name of the initial branch(es) in the newly created project.
+	Several branches can be specified on the command line.
+	If several branches are specified then the first one becomes HEAD
+	of the project. If none branches are specified then default value
+	('master') is used.
 
 --owner::
 -o::
@@ -163,7 +166,8 @@
 
 REPLICATION
 -----------
-The remote repository creation is performed by a Bourne shell script:
+If the replication plugin is installed, the plugin will attempt to
+perform remote repository creation by a Bourne shell script:
 
 ====
   mkdir -p '/base/project.git' && cd '/base/project.git' && git init --bare && git update-ref HEAD refs/heads/master
@@ -174,10 +178,13 @@
 environment variable.  Administrators could also run this command line
 by hand to establish a new empty repository.
 
+A custom extension or plugin may also be developed to implement the
+NewProjectCreatedListener extension point and handle custom logic
+for remote repository creation.
+
 SEE ALSO
 --------
 
-* link:config-replication.html[Git Replication/Mirroring]
 * link:project-setup.html[Project Setup]
 
 GERRIT
diff --git a/Documentation/cmd-hook-commit-msg.txt b/Documentation/cmd-hook-commit-msg.txt
index 6318bba..bd602c1 100644
--- a/Documentation/cmd-hook-commit-msg.txt
+++ b/Documentation/cmd-hook-commit-msg.txt
@@ -54,7 +54,7 @@
 OBTAINING
 ---------
 To obtain the 'commit-msg' script use scp, wget or curl to download it
-to your local system from your gerrit server.
+to your local system from your Gerrit server.
 
 You can use either of the below commands:
 
@@ -73,6 +73,12 @@
   $ curl -o ~/duhproject/.git/hooks/commit-msg http://review.example.com/tools/hooks/commit-msg
 ====
 
+Make sure the hook file is executable:
+
+====
+  $ chmod u+x ~/duhproject/.git/hooks/commit-msg
+====
+
 SEE ALSO
 --------
 
diff --git a/Documentation/cmd-index.txt b/Documentation/cmd-index.txt
index b09c3b3..ccd9ffc 100644
--- a/Documentation/cmd-index.txt
+++ b/Documentation/cmd-index.txt
@@ -12,8 +12,8 @@
   $ scp -p -P 29418 john.doe@review.example.com:bin/gerrit-cherry-pick ~/bin/
   $ scp -p -P 29418 john.doe@review.example.com:hooks/commit-msg .git/hooks/
 
-  $ curl http://review.example.com/tools/bin/gerrit-cherry-pick
-  $ curl http://review.example.com/tools/hooks/commit-msg
+  $ curl -o ~/bin/gerrit-cherry-pick http://review.example.com/tools/bin/gerrit-cherry-pick
+  $ curl -o .git/hooks/commit-msg http://review.example.com/tools/hooks/commit-msg
 
 For more details on how to determine the correct SSH port number,
 see link:user-upload.html#test_ssh[Testing Your SSH Connection].
@@ -54,6 +54,9 @@
 'gerrit approve'::
 	'Deprecated alias for `gerrit review`.'
 
+link:cmd-ban-commit.html[gerrit ban-commit]::
+	Bans a commit from a project's repository.
+
 link:cmd-ls-groups.html[gerrit ls-groups]::
 	List groups visible to the caller.
 
@@ -93,21 +96,24 @@
 link:cmd-create-account.html[gerrit create-account]::
 	Create a new batch/role account.
 
+link:cmd-set-account.html[gerrit set-account]::
+	Change an account's settings.
+
 link:cmd-create-group.html[gerrit create-group]::
 	Create a new account group.
 
 link:cmd-create-project.html[gerrit create-project]::
 	Create a new project and associated Git repository.
 
+link:cmd-set-project.html[gerrit set-project]::
+    Change a project's settings.
+
 link:cmd-flush-caches.html[gerrit flush-caches]::
 	Flush some/all server caches from memory.
 
 link:cmd-gsql.html[gerrit gsql]::
 	Administrative interface to active database.
 
-link:cmd-replicate.html[gerrit replicate]::
-	Manually trigger replication, to recover a node.
-
 link:cmd-set-project-parent.html[gerrit set-project-parent]::
 	Change the project permissions are inherited from.
 
@@ -120,6 +126,30 @@
 link:cmd-show-queue.html[gerrit show-queue]::
 	Display the background work queues, including replication.
 
+link:cmd-plugin-install.html[gerrit plugin add]::
+    Alias for 'gerrit plugin install'.
+
+link:cmd-plugin-enable.html[gerrit plugin enable]::
+    Enable plugins.
+
+link:cmd-plugin-install.html[gerrit plugin install]::
+    Install/Add a plugin.
+
+link:cmd-plugin-ls.html[gerrit plugin ls]::
+    List the installed plugins.
+
+link:cmd-plugin-reload.html[gerrit plugin reload]::
+    Reload/Restart plugins.
+
+link:cmd-plugin-remove.html[gerrit plugin remove]::
+    Disable plugins.
+
+link:cmd-plugin-remove.html[gerrit plugin rm]::
+    Alias for 'gerrit plugin remove'.
+
+link:cmd-test-submit-rule.html[gerrit test-submit-rule]::
+	Test prolog submit rules.
+
 link:cmd-kill.html[kill]::
 	Kills a scheduled or running task.
 
diff --git a/Documentation/cmd-ls-groups.txt b/Documentation/cmd-ls-groups.txt
index 8564db2..306bb92 100644
--- a/Documentation/cmd-ls-groups.txt
+++ b/Documentation/cmd-ls-groups.txt
@@ -9,10 +9,11 @@
 --------
 [verse]
 'ssh' -p <port> <host> 'gerrit ls-groups'
-  [--project <NAME>]
-  [--user <NAME>]
+  [--project <NAME> | -p <NAME>]
+  [--user <NAME> | -u <NAME>]
   [--visible-to-all]
-  [--type {internal | ldap | system}]
+  [--type {internal | system}]
+  [--verbose | -v]
 
 DESCRIPTION
 -----------
@@ -30,6 +31,12 @@
 ---------
 This command is intended to be used in scripts.
 
+All non-printable characters (ASCII value 31 or less) are escaped
+according to the conventions used in languages like C, Python, and Perl,
+employing standard sequences like `\n` and `\t`, and `\xNN` for all
+others. In shell scripts, the `printf` command can be used to unescape
+the output.
+
 OPTIONS
 -------
 --project::
@@ -65,10 +72,19 @@
 +
 --
 `internal`:: Any group defined within Gerrit.
-`ldap`:: Any group defined by an external LDAP database.
 `system`:: Any system defined and managed group.
 --
 
+--verbose::
+-v::
+	Enable verbose output with tab-separated columns for the
+	group name, UUID, description, type (`SYSTEM` or `INTERNAL`),
+	owner group name, owner group UUID and whether the group is
+	visible to all (`true` or `false`).
++
+If a group has been "orphaned", i.e. its owner group UUID refers to a
+nonexistent group, the owner group name field will read `n/a`.
+
 EXAMPLES
 --------
 
@@ -91,6 +107,23 @@
 	Registered Users
 =====
 
+Extract the UUID of the 'Administrators' group:
+
+=====
+	$ ssh -p 29418 review.example.com gerrit ls-groups -v | awk '-F\t' '$1 == "Administrators" {print $2}'
+	ad463411db3eec4e1efb0d73f55183c1db2fd82a
+=====
+
+Extract and expand the multi-line description of the 'Administrators'
+group:
+
+=====
+	$ printf "$(ssh -p 29418 review.example.com gerrit ls-groups -v | awk '-F\t' '$1 == "Administrators" {print $3}')\n"
+	This is a
+	multi-line
+	description.
+=====
+
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/cmd-ls-projects.txt b/Documentation/cmd-ls-projects.txt
index 7782aa8..d7d5aa5 100644
--- a/Documentation/cmd-ls-projects.txt
+++ b/Documentation/cmd-ls-projects.txt
@@ -10,8 +10,12 @@
 [verse]
 'ssh' -p <port> <host> 'gerrit ls-projects'
   [--show-branch <BRANCH> ...]
-  [--tree]
+  [--description | -d]
+  [--tree | -t]
   [--type {code | permissions | all}]
+  [--format {text | json | json_compact}]
+  [--all]
+  [--limit <N>]
 
 DESCRIPTION
 -----------
@@ -23,7 +27,7 @@
 
 ACCESS
 ------
-Any user who has configured an SSH key.
+Any user who has configured an SSH key, or by an user over HTTP.
 
 SCRIPTING
 ---------
@@ -42,12 +46,15 @@
 	whole project is not shown.
 
 --description::
---d::
+-d::
 	Allows listing of projects together with their respective
 	description.
 +
-Line-feeds are escaped to allow ls-project to keep the
-"one project per line"-style.
+For text format output, all non-printable characters (ASCII value 31 or
+less) are escaped according to the conventions used in languages like C,
+Python, and Perl, employing standard sequences like `\n` and `\t`, and
+`\xNN` for all others. In shell scripts, the `printf` command can be
+used to unescape the output.
 
 --tree::
 -t::
@@ -64,6 +71,15 @@
 `all`:: Any type of project.
 --
 
+--format::
+	What output format to display the results in.
++
+--
+`text`:: Simple text based format.
+`json`:: Map of JSON objects describing each project.
+`json_compact`:: Minimized JSON output.
+--
+
 --all::
 	Display all projects that are accessible by the calling user
 	account. Besides the projects that the calling user account has
@@ -72,12 +88,43 @@
 	the 'READ' access right is not assigned to the calling user
 	account).
 
+--limit::
+	Cap the number of results to the first N matches.
+
+HTTP
+----
+This command is also available over HTTP, as `/projects/` for
+anonymous access and `/a/projects/` for authenticated access.
+Named options are available as query parameters. Results can
+be limited to projects matching a prefix by supplying the prefix
+as part of the URL, for example `/projects/external/` lists only
+projects whose name start with the string `external/`.
+
+Over HTTP the `json_compact` output format is assumed if the client
+explicitly asks for JSON using HTTP header `Accept: application/json`.
+When any JSON output format is used on HTTP, readers must skip the
+first line produced. The first line is a garbage JSON string crafted
+to prevent a browser from executing the response in a script tag.
+
+Output will be gzip compressed if `Accept-Encoding: gzip` was used
+by the client in the request headers.
+
 EXAMPLES
 --------
 
 List visible projects:
 =====
 	$ ssh -p 29418 review.example.com gerrit ls-projects
+	platform/manifest
+	tools/gerrit
+	tools/gwtorm
+
+	$ curl http://review.example.com/projects/
+	platform/manifest
+	tools/gerrit
+	tools/gwtorm
+
+	$ curl http://review.example.com/projects/tools/
 	tools/gerrit
 	tools/gwtorm
 =====
diff --git a/Documentation/cmd-plugin-enable.txt b/Documentation/cmd-plugin-enable.txt
new file mode 100644
index 0000000..da651ca
--- /dev/null
+++ b/Documentation/cmd-plugin-enable.txt
@@ -0,0 +1,44 @@
+plugin enable
+=============
+
+NAME
+----
+plugin enable - Enable plugins.
+
+SYNOPSIS
+--------
+[verse]
+'ssh' -p <port> <host> 'gerrit plugin enable'
+  <NAME> ...
+
+DESCRIPTION
+-----------
+Enable plugins currently disabled. The plugins will be enabled by renaming
+the plugin jars in the site path's `plugins` directory from
+`<plugin-jar-name>.disabled` to `<plugin-jar-name>`.
+
+ACCESS
+------
+Caller must be a member of the privileged 'Administrators' group.
+
+SCRIPTING
+---------
+This command is intended to be used in scripts.
+
+OPTIONS
+-------
+<NAME>::
+	Name of the plugin that should be enabled.  Multiple names of
+	plugins that should be enabled may be specified.
+
+EXAMPLES
+--------
+Enable a plugin:
+
+====
+	ssh -p 29418 localhost gerrit plugin enable my-plugin
+====
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/cmd-plugin-install.txt b/Documentation/cmd-plugin-install.txt
new file mode 100644
index 0000000..79d1f4a
--- /dev/null
+++ b/Documentation/cmd-plugin-install.txt
@@ -0,0 +1,71 @@
+plugin install
+==============
+
+NAME
+----
+plugin install - Install/Add a plugin.
+
+plugin add - Install/Add a plugin.
+
+SYNOPSIS
+--------
+[verse]
+'ssh' -p <port> <host> 'gerrit plugin install | add'
+  [--name <NAME> | -n <NAME>]
+  - | <URL> | <PATH>
+
+DESCRIPTION
+-----------
+Install/Add a plugin. The plugin will be copied into the site path's
+`plugins` directory.
+
+ACCESS
+------
+Caller must be a member of the privileged 'Administrators' group.
+
+SCRIPTING
+---------
+This command is intended to be used in scripts.
+
+OPTIONS
+-------
+-::
+	Plugin jar as piped input.
+
+<URL>::
+	URL from where the plugin should be downloaded. This can be an
+	HTTP or FTP site.
+
+<PATH>::
+	Absolute file path to the plugin jar.
+
+--name::
+-n::
+	The name under which the plugin should be installed.
+
+EXAMPLES
+--------
+Install a plugin from an absolute file path on the server's host:
+
+====
+	ssh -p 29418 localhost gerrit plugin install -n name \
+	  $(pwd)/my-plugin.jar
+====
+
+Install a plugin from an HTTP site:
+
+====
+	ssh -p 29418 localhost gerrit plugin install -n name \
+	  http://build-server/output/our-plugin.jar
+====
+
+Install a plugin from piped input:
+
+====
+	ssh -p 29418 localhost gerrit plugin install -n name \
+	  - <target/name-0.1.jar
+====
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/cmd-plugin-ls.txt b/Documentation/cmd-plugin-ls.txt
new file mode 100644
index 0000000..6cce83c
--- /dev/null
+++ b/Documentation/cmd-plugin-ls.txt
@@ -0,0 +1,44 @@
+plugin ls
+=========
+
+NAME
+----
+plugin ls - List the installed plugins.
+
+SYNOPSIS
+--------
+[verse]
+'ssh' -p <port> <host> 'gerrit plugin ls'
+  [--all | -a]
+  [--format {text | json | json_compact}]
+
+DESCRIPTION
+-----------
+List the installed plugins and show their version and status.
+
+ACCESS
+------
+Caller must be a member of the privileged 'Administrators' group.
+
+SCRIPTING
+---------
+This command is intended to be used in scripts.
+
+OPTIONS
+-------
+--all::
+-a::
+	List all plugins, including disabled plugins.
+
+--format::
+	What output format to display the results in.
++
+--
+`text`:: Simple text based format.
+`json`:: Map of JSON objects describing each project.
+`json_compact`:: Minimized JSON output.
+--
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/cmd-plugin-reload.txt b/Documentation/cmd-plugin-reload.txt
new file mode 100644
index 0000000..3932e30
--- /dev/null
+++ b/Documentation/cmd-plugin-reload.txt
@@ -0,0 +1,48 @@
+plugin reload
+=============
+
+NAME
+----
+plugin reload - Reload/Restart plugins.
+
+SYNOPSIS
+--------
+[verse]
+'ssh' -p <port> <host> 'gerrit plugin reload'
+  <NAME> ...
+
+DESCRIPTION
+-----------
+Reload/Restart plugins.
+
+Whether a plugin is reloaded or restarted is defined by the plugin's
+link:dev-plugins.html#reload_method[reload method].
+
+E.g. a plugin needs to be reloaded if its configuration is modified to
+make the new configuration data become active.
+
+ACCESS
+------
+Caller must be a member of the privileged 'Administrators' group.
+
+SCRIPTING
+---------
+This command is intended to be used in scripts.
+
+OPTIONS
+-------
+<NAME>::
+	Name of the plugin that should be reloaded.  Multiple names of
+	plugins that should be reloaded may be specified.
+
+EXAMPLES
+--------
+Reload a plugin:
+
+====
+	ssh -p 29418 localhost gerrit plugin reload my-plugin
+====
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/cmd-plugin-remove.txt b/Documentation/cmd-plugin-remove.txt
new file mode 100644
index 0000000..ab8f95b
--- /dev/null
+++ b/Documentation/cmd-plugin-remove.txt
@@ -0,0 +1,45 @@
+plugin remove
+=============
+
+NAME
+----
+plugin remove - Disable plugins.
+
+plugin rm - Disable plugins.
+
+SYNOPSIS
+--------
+[verse]
+'ssh' -p <port> <host> 'gerrit plugin remove | rm'
+  <NAME> ...
+
+DESCRIPTION
+-----------
+Disable plugins. The plugins will be disabled by renaming the plugin
+jars in the site path's `plugins` directory to `<plugin-jar-name>.disabled`.
+
+ACCESS
+------
+Caller must be a member of the privileged 'Administrators' group.
+
+SCRIPTING
+---------
+This command is intended to be used in scripts.
+
+OPTIONS
+-------
+<NAME>::
+	Name of the plugin that should be disabled.  Multiple names of
+	plugins that should be disabled may be specified.
+
+EXAMPLES
+--------
+Disable a plugin:
+
+====
+	ssh -p 29418 localhost gerrit plugin remove my-plugin
+====
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/cmd-query.txt b/Documentation/cmd-query.txt
index 253bed1..2feea11 100644
--- a/Documentation/cmd-query.txt
+++ b/Documentation/cmd-query.txt
@@ -16,6 +16,7 @@
   [--comments]
   [--commit-message]
   [--dependencies]
+  [--submit-records]
   [--]
   <query>
   [limit:<n>]
@@ -80,6 +81,11 @@
 	Show information about patch sets which depend on, or are needed by,
 	each patch set.
 
+--submit-records::
+	Show submit record information about the change, which
+	includes whether the change meets the criteria for submission
+	(including information for each review label).
+
 limit:<n>::
 	Maximum number of results to return.  This is actually a
 	query operator, and not a command line option.	If more
@@ -124,7 +130,7 @@
 ------
 The JSON messages consist of nested objects referencing the
 link:json.html#change[change],
-link:json.html#patchset[patchset],
+link:json.html#patchSet[patchset],
 link:json.html#[account]
 involved, and other attributes as appropriate.
 
diff --git a/Documentation/cmd-receive-pack.txt b/Documentation/cmd-receive-pack.txt
index 7e5ca09..68f686d 100644
--- a/Documentation/cmd-receive-pack.txt
+++ b/Documentation/cmd-receive-pack.txt
@@ -8,7 +8,10 @@
 SYNOPSIS
 --------
 [verse]
-'git receive-pack' [--reviewer <address>] [--cc <address>] <project>
+'git receive-pack'
+  [--reviewer <address> | --re <address>]
+  [--cc <address>]
+  <project>
 
 DESCRIPTION
 -----------
diff --git a/Documentation/cmd-replicate.txt b/Documentation/cmd-replicate.txt
deleted file mode 100644
index 7722027..0000000
--- a/Documentation/cmd-replicate.txt
+++ /dev/null
@@ -1,103 +0,0 @@
-gerrit replicate
-================
-
-NAME
-----
-gerrit replicate - Manually trigger replication, to recover a node
-
-SYNOPSIS
---------
-[verse]
-'ssh' -p <port> <host> 'gerrit replicate'
-  [--url <PATTERN>]
-  {--all | <PROJECT> ...}
-
-DESCRIPTION
------------
-Schedules replication of the specified projects to all configured
-replication destinations, or only those whose URLs match the pattern
-given on the command line.
-
-Normally Gerrit automatically schedules replication whenever it
-makes a change to a managed Git repository.  However, there are
-other reasons why an administrator may wish to trigger replication:
-
-* Destination disappears, then later comes back online.
-+
-If a destination went offline for a period of time, when it comes
-back, it may be missing commits that it should have.  Triggering a
-replication run for all projects against that URL will update it.
-
-* After repacking locally, and using `rsync` to distribute the new
-  pack files to the destinations.
-+
-If the local server is repacked, and then the resulting pack files
-are sent to remote peers using `rsync -a --delete-after`, there
-is a chance that the rsync missed a change that was added during
-the rsync data transfer, and the rsync will remove that changes's
-data from the remote, even though the automatic replication pushed
-it there in parallel to the rsync.
-+
-Its a good idea to run replicate with `--all` to ensure all
-projects are consistent after the rsync is complete.
-
-* After deleting a ref by hand.
-+
-If a ref must be removed (e.g. to purge a change or patch set
-that shouldn't have been created, and that must be eradicated)
-that delete must be done by direct git access on the local,
-managed repository.  Gerrit won't know about the delete, and is
-unable to replicate it automatically.  Triggering replication on
-just the affected project can update the mirrors.
-
-ACCESS
-------
-Caller must be a member of the privileged 'Administrators' group,
-or have been granted
-link:access-control.html#capability_startReplication[the 'Start Replication' global capability].
-
-SCRIPTING
----------
-This command is intended to be used in scripts.
-
-OPTIONS
--------
---all::
-	Schedule replicating for all projects.
-
---url <PATTERN>::
-	Replicate only to replication destinations whose URL
-	contains the substring <PATTERN>.  This can be useful to
-	replicate only to a previously down node, which has been
-	brought back online.
-
-EXAMPLES
---------
-Replicate every project, to every configured remote:
-
-====
-  $ ssh -p 29418 review.example.com gerrit replicate --all
-====
-
-Replicate only to `srv2` now that it is back online:
-
-====
-  $ ssh -p 29418 review.example.com gerrit replicate --url srv2 --all
-====
-
-Replicate only the `tools/gerrit` project, after deleting a ref
-locally by hand:
-
-====
-  $ git --git-dir=/home/git/tools/gerrit.git update-ref -d refs/changes/00/100/1
-  $ ssh -p 29418 review.example.com gerrit replicate tools/gerrit
-====
-
-SEE ALSO
---------
-
-* link:config-replication.html[Git Replication/Mirroring]
-
-GERRIT
-------
-Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/cmd-review.txt b/Documentation/cmd-review.txt
index ac613e5..513bc6e 100644
--- a/Documentation/cmd-review.txt
+++ b/Documentation/cmd-review.txt
@@ -9,10 +9,10 @@
 --------
 [verse]
 'ssh' -p <port> <host> 'gerrit review'
-  [--project <PROJECT>]
-  [--message <MESSAGE>]
+  [--project <PROJECT> | -p <PROJECT>]
+  [--message <MESSAGE> | -m <MESSAGE>]
   [--force-message]
-  [--submit]
+  [--submit | -s]
   [--abandon | --restore]
   [--publish]
   [--delete]
@@ -52,16 +52,19 @@
 
 --force-message::
 	Option which allows Gerrit to publish the --message, even
-	when the labels could not be applied due to change being
-	closed).
+	when the labels could not be applied due to the change being
+	closed.
 +
 Used by some scripts/CI-systems, where the results (or links
 to the result) are posted as a message after completion of a
 build (often together with a label-change, indicating the success
 of the build).
 +
-If the message is posted successfully, the cmd will return
+If the message is posted successfully, the command will return
 successfully, even if the label could not be changed.
++
+This option will not force the message to be posted if the command
+fails because the user is not permitted to change the label.
 
 --help::
 -h::
@@ -69,11 +72,11 @@
 	complete listing of supported approval categories and values.
 
 --abandon::
-	Abandon the specified patch set(s).
+	Abandon the specified change(s).
 	(option is mutually exclusive with --submit and --restore)
 
 --restore::
-	Restore the specified abandoned patch set(s).
+	Restore the specified abandoned change(s).
 	(option is mutually exclusive with --abandon)
 
 --submit::
diff --git a/Documentation/cmd-set-account.txt b/Documentation/cmd-set-account.txt
new file mode 100644
index 0000000..f9855cd
--- /dev/null
+++ b/Documentation/cmd-set-account.txt
@@ -0,0 +1,96 @@
+gerrit set-account
+==================
+
+NAME
+----
+gerrit set-account - Change an account's settings.
+
+SYNOPSIS
+--------
+[verse]
+set-account [--full-name <FULLNAME>] [--active|--inactive] \
+            [--add-email <EMAIL>] [--delete-email <EMAIL> | ALL] \
+            [--add-ssh-key - | <KEY>] \
+            [--delete-ssh-key - | <KEY> | ALL] \
+            [--http-password <PASSWORD>] <USER>
+
+DESCRIPTION
+-----------
+Modifies a given user's settings. This command can be useful to
+deactivate an account, set HTTP password, add/delete ssh keys without
+going through the UI.
+
+It also allows managing email addresses, which bypasses the
+verification step we force within the UI.
+
+ACCESS
+------
+Caller must be a member of the privileged 'Administrators' group.
+
+SCRIPTING
+---------
+This command is intended to be used in scripts.
+
+OPTIONS
+-------
+<USER>::
+    Required; Full name, email-address, SSH username or account id.
+
+--full-name::
+    Display name of the user account.
++
+Names containing spaces should be quoted in single quotes (').
+This most likely requires double quoting the value, for example
+`--full-name "'A description string'"`.
+
+--active::
+    Set the account state to be active.
+
+--inactive::
+    Set the account state to be inactive. This prevents the
+    user from logging in.
+
+--add-email::
+    Add another email to the user's account. This doesn't
+    trigger the mail validation and adds the email directly
+    to the user's account.
+    May be supplied more than once to add multiple emails to
+    an account in a single command execution.
+
+--delete-email::
+    Delete an email from this user's account if it exists.
+    If the email provided is 'ALL', all associated emails are
+    deleted from this account.
+    Maybe supplied more than once to remove multiple emails
+    from an account in a single command execution.
+
+--add-ssh-key::
+    Content of the public SSH key to add to the account's
+    keyring.  If `-` the key is read from stdin, rather than
+    from the command line.
+    May be supplied more than once to add multiple SSH keys
+    in a single command execution.
+
+--delete-ssh-key::
+    Content of the public SSH key to remove from the account's
+    keyring or the comment associated with this key.
+    If `-` the key is read from stdin, rather than from the
+    command line. If the key provided is 'ALL', all
+    associated SSH keys are removed from this account.
+    May be supplied more than once to delete multiple SSH
+    keys in a single command execution.
+
+--http-password::
+    Set the HTTP password for the user account.
+
+EXAMPLES
+--------
+Add an email and SSH key to `watcher`'s account:
+
+====
+    $ cat ~/.ssh/id_watcher.pub | ssh -p 29418 review.example.com gerrit set-account --add-ssh-key - --add-email mail@example.com watcher
+====
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/cmd-set-project.txt b/Documentation/cmd-set-project.txt
new file mode 100644
index 0000000..059f063
--- /dev/null
+++ b/Documentation/cmd-set-project.txt
@@ -0,0 +1,111 @@
+gerrit set-project
+==================
+
+NAME
+----
+gerrit set-project - Change a project's settings.
+
+SYNOPSIS
+--------
+[verse]
+'ssh' -p <port> <host> 'gerrit set-project'
+  [--description <DESC> | -d <DESC>]
+  [--submit-type <TYPE> | -t <TYPE>]
+  [--use|no-contributor-agreements | --ca|nca]
+  [--use|no-signed-off-by | --so|nso]
+  [--use|no-content-merge]
+  [--require|no-change-id | --id|nid]
+  [--project-state | --ps]
+  <NAME>
+
+DESCRIPTION
+-----------
+Modifies a given project's settings. This command can be useful to
+batch change projects.
+
+The command is argument-safe, that is, if no argument is given the
+previous settings are kept intact.
+
+ACCESS
+------
+Caller must be a member of the privileged 'Administrators' group.
+
+SCRIPTING
+---------
+This command is intended to be used in scripts.
+
+OPTIONS
+-------
+<NAME>::
+    Required; name of the project to edit.  If name ends
+    with `.git` the suffix will be automatically removed.
+
+--description::
+-d::
+    New description of the project.  If not specified,
+    the old description is kept.
++
+Description values containing spaces should be quoted in single quotes
+(').  This most likely requires double quoting the value, for example
+`--description "'A description string'"`.
+
+--submit-type::
+-t::
+    Action used by Gerrit to submit an approved change to its
+    destination branch.  Supported options are:
++
+* FAST_FORWARD_ONLY: produces a strictly linear history.
+* MERGE_IF_NECESSARY: create a merge commit when required.
+* MERGE_ALWAYS: always create a merge commit.
+* CHERRY_PICK: always cherry-pick the commit.
+
++
+For more details see
+link:project-setup.html#submit_type[Change Submit Actions].
+
+--use|no-content-merge::
+    If enabled, Gerrit will try to perform a 3-way merge of text
+    file content when a file has been modified by both the
+    destination branch and the change being submitted.  This
+    option only takes effect if submit type is not
+    FAST_FORWARD_ONLY.
+
+--use|no-contributor-agreements::
+--ca|nca::
+    If enabled, authors must complete a contributor agreement
+    on the site before pushing any commits or changes to this
+    project.
+
+--use|no-signed-off-by::
+--so|nso:
+    If enabled, each change must contain a Signed-off-by line
+    from either the author or the uploader in the commit message.
+
+--require|no-change-id::
+--id|nid::
+    Require a valid link:user-changeid.html[Change-Id] footer
+    in any commit uploaded for review. This does not apply to
+    commits pushed directly to a branch or tag.
+
+--project-state::
+--ps::
+    Set project's visibility.
++
+* ACTIVE: project is regular and is the default value.
+* READ_ONLY: users can see the project if read permission
+is granted, but all modification operations are disabled.
+* HIDDEN: the project is not visible for those who are not owners
+
+EXAMPLES
+--------
+Change project `example` to be hidden, require change id, don't use content merge
+and use 'merge if necessary' as merge strategy:
+
+====
+    $ ssh -p 29418 review.example.com gerrit set-project example --submit-type MERGE_IF_NECESSARY\
+    --require-change-id --no-content-merge --project-state HIDDEN
+====
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
\ No newline at end of file
diff --git a/Documentation/cmd-set-reviewers.txt b/Documentation/cmd-set-reviewers.txt
index 9e08e39..32fd35e 100644
--- a/Documentation/cmd-set-reviewers.txt
+++ b/Documentation/cmd-set-reviewers.txt
@@ -9,9 +9,9 @@
 --------
 [verse]
 'ssh' -p <port> <host> 'gerrit set-reviewers'
-  [--project <PROJECT>]
-  [--add REVIEWER ...]
-  [--remove REVIEWER ...]
+  [--project <PROJECT> | -p <PROJECT>]
+  [--add <REVIEWER> ... | -a <REVIEWER> ...]
+  [--remove <REVIEWER> ... | -r <REVIEWER> ...]
   [--]
   {COMMIT | CHANGE-ID}...
 
diff --git a/Documentation/cmd-show-connections.txt b/Documentation/cmd-show-connections.txt
index b5d41bd..8404a97 100644
--- a/Documentation/cmd-show-connections.txt
+++ b/Documentation/cmd-show-connections.txt
@@ -8,7 +8,7 @@
 SYNOPSIS
 --------
 [verse]
-'ssh' -p <port> <host> 'gerrit show-connections' [-n]
+'ssh' -p <port> <host> 'gerrit show-connections' [--numeric | -n]
 
 DESCRIPTION
 -----------
diff --git a/Documentation/cmd-stream-events.txt b/Documentation/cmd-stream-events.txt
index bf78051..a8cf3b0 100644
--- a/Documentation/cmd-stream-events.txt
+++ b/Documentation/cmd-stream-events.txt
@@ -41,9 +41,10 @@
 SCHEMA
 ------
 The JSON messages consist of nested objects referencing the *change*,
-*patchset*, *account* involved, and other attributes as appropriate.
+*patchSet*, *account* involved, and other attributes as appropriate.
 The currently supported message types are *patchset-created*,
-*comment-added*, *change-merged*, and *change-abandoned*.
+*draft-published*, *change-abandoned*, *change-restored*,
+*change-merged*, *comment-added* and *ref-updated*.
 
 Note that any field may be missing in the JSON messages, so consumers of
 this JSON stream should deal with that appropriately.
@@ -56,7 +57,17 @@
 
 change:: link:json.html#change[change attribute]
 
-patchset:: link:json.html#patchset[patchset attribute]
+patchSet:: link:json.html#patchSet[patchSet attribute]
+
+uploader:: link:json.html#account[account attribute]
+
+Draft Published
+^^^^^^^^^^^^^^^
+type:: "draft-published"
+
+change:: link:json.html#change[change attribute]
+
+patchset:: link:json.html#patchSet[patchset attribute]
 
 uploader:: link:json.html#account[account attribute]
 
@@ -66,27 +77,31 @@
 
 change:: link:json.html#change[change attribute]
 
-patchset:: link:json.html#patchset[patchset attribute]
+patchSet:: link:json.html#patchSet[patchSet attribute]
 
 abandoner:: link:json.html#account[account attribute]
 
+reason:: Reason for abandoning the change.
+
 Change Restored
-^^^^^^^^^^^^^^^^
+^^^^^^^^^^^^^^^
 type:: "change-restored"
 
 change:: link:json.html#change[change attribute]
 
-patchset:: link:json.html#patchset[patchset attribute]
+patchSet:: link:json.html#patchSet[patchSet attribute]
 
 restorer:: link:json.html#account[account attribute]
 
+reason:: Reason for restoring the change.
+
 Change Merged
 ^^^^^^^^^^^^^
 type:: "change-merged"
 
 change:: link:json.html#change[change attribute]
 
-patchset:: link:json.html#patchset[patchset attribute]
+patchSet:: link:json.html#patchSet[patchSet attribute]
 
 submitter:: link:json.html#account[account attribute]
 
@@ -96,10 +111,12 @@
 
 change:: link:json.html#change[change attribute]
 
-patchset:: link:json.html#patchset[patchset attribute]
+patchSet:: link:json.html#patchSet[patchSet attribute]
 
 author:: link:json.html#account[account attribute]
 
+approvals:: All link:json.html#approval[approval attributes] granted.
+
 comment:: Comment text author had written
 
 Ref Updated
@@ -108,7 +125,7 @@
 
 submitter:: link:json.html#account[account attribute]
 
-refUpdate:: link:json.html#refupdate[refupdate attribute]
+refUpdate:: link:json.html#refUpdate[refUpdate attribute]
 
 
 SEE ALSO
diff --git a/Documentation/cmd-test-submit-rule.txt b/Documentation/cmd-test-submit-rule.txt
new file mode 100644
index 0000000..5b70bd1
--- /dev/null
+++ b/Documentation/cmd-test-submit-rule.txt
@@ -0,0 +1,93 @@
+gerrit test-submit-rule
+=======================
+
+NAME
+----
+gerrit test-submit-rule - Test prolog submit rules with a chosen changeset.
+
+SYNOPSIS
+--------
+[verse]
+'ssh' -p <port> <host> 'gerrit test-submit-rule'
+  [-s]
+  [--no-filters]
+  [--format {TEXT | JSON}]
+  CHANGE
+
+DESCRIPTION
+-----------
+Provides a way to test prolog link:prolog-cookbook.html[submit rules].
+
+OPTIONS
+-------
+-s::
+	Reads a rules.pl file from stdin instead of rules.pl in refs/meta/config.
+
+--no-filters::
+	Don't run the submit_filter/2 from the parent projects of the specified change.
+
+--format::
+  What output format to display the results in.
++
+--
+`text`:: Simple text based format.
+`json`:: A JSON object described in link:json.html#submitRecord[submit record].
+`json_compact`:: Minimized JSON output.
+--
+
+ACCESS
+------
+Can be used by anyone that has permission to read the specified changeset.
+
+EXAMPLES
+--------
+
+
+Test submit_rule from stdin.
+====
+ $ cat non-author-codereview.pl | ssh -p 29418 review.example.com gerrit test-submit-rule -s I78f2c6673db24e4e92ed32f604c960dc952437d9
+ Non-Author-Code-Review: NOT_READY
+ Verified: NOT_READY
+ Code-Review: NOT_READY by Anonymous Coward <test@email.com>
+
+ NOT_READY
+====
+
+Test submit_rule from stdin and return the results as JSON.
+====
+ cat non-author-codereview.pl | ssh -p 29418 review.example.com gerrit test-submit-rule --format=JSON -s I78f2c6673db24e4e92ed32f604c960dc952437d9
+ {
+  "approvals": [
+    {
+      "type": "Verified",
+      "value": "NEED"
+    },
+    {
+      "type": "Code-Review",
+      "value": "OK",
+      "by": {
+        "email": "test@email.com",
+        "username": "test"
+      }
+    }
+  ],
+  "value": "NOT_READY"
+ }
+====
+
+Test the active submit_rule from the refs/meta/config branch, ignoring filters in the project parents.
+====
+ $ ssh -p 29418 review.example.com gerrit test-submit-rule I78f2c6673db24e4e92ed32f604c960dc952437d9 --no-filters
+ Verified: NOT_READY
+ Code-Review: NOT_READY by Anonymous Coward <test@email.com>
+
+ NOT_READY
+====
+
+SCRIPTING
+---------
+Can be used either interactively for testing new prolog submit rules, or from a script to check the submit status of a change.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/config-contact.txt b/Documentation/config-contact.txt
index 5c633cf..4d8851f 100644
--- a/Documentation/config-contact.txt
+++ b/Documentation/config-contact.txt
@@ -48,7 +48,7 @@
 The actual values chosen don't matter later, and are only to help
 document the purpose of the key.
 
-Chose a fairly long expiration period, such as 20 years.  For most
+Choose a fairly long expiration period, such as 20 years.  For most
 Gerrit instances, contact data will be written once, and rarely,
 if ever, read back.
 
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 535aaa8..de2aa02 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -88,6 +88,12 @@
 provider chosen by the end-user.  For more information see
 http://openid.net/[openid.net].
 +
+* `OpenID_SSO`
++
+Supports OpenID from a single provider.  There is no registration
+link, and the "Sign In" link sends the user directly to the provider's
+SSO entry point.
++
 * `HTTP`
 +
 Gerrit relies upon data presented in the HTTP request.  This includes
@@ -107,7 +113,7 @@
 * `CLIENT_SSL_CERT_LDAP`
 +
 This authentication type is actually kind of SSO. Gerrit will configure
-Jetty's SSL channel to request client's SSL certificate. For this
+Jetty's SSL channel to request the client's SSL certificate. For this
 authentication to work a Gerrit administrator has to import the root
 certificate of the trust chain used to issue the client's certificate
 into the <review-site>/etc/keystore.
@@ -161,7 +167,7 @@
 +
 List of permitted OpenID providers.  A user may only authenticate
 with an OpenID that matches this list.  Only used if `auth.type`
-was set to OpenID (the default).
+is set to OpenID (the default).
 +
 Patterns may be either a
 link:http://download.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html[standard
@@ -173,7 +179,7 @@
 
 [[auth.trustedOpenID]]auth.trustedOpenID::
 +
-List of trusted OpenID providers.  Only used if `auth.type` was
+List of trusted OpenID providers.  Only used if `auth.type` is
 set to OpenID (the default).
 +
 In order for a user to take advantage of permissions beyond those
@@ -229,10 +235,17 @@
 +
 Default is 12 hours.
 
+[[auth.openIdSsoUrl]]auth.openIdSsoUrl::
++
+The SSO entry point URL.  Only used if `auth.type` was set to
+OpenID_SSO.
++
+The "Sign In" link will send users directly to this URL.
+
 [[auth.httpHeader]]auth.httpHeader::
 +
 HTTP header to trust the username from, or unset to select HTTP basic
-or digest authentication.  Only used if `auth.type` was set to HTTP.
+or digest authentication.  Only used if `auth.type` is set to HTTP.
 
 [[auth.logoutUrl]]auth.logoutUrl::
 +
@@ -319,6 +332,18 @@
 +
 By default this is set to false.
 
+[[auth.gitBasicAuth]]auth.gitBasicAuth::
++
+If true then Git over HTTP and HTTP/S traffic is authenticated using
+standard BasicAuth and credentials validated using the same auth
+method configured for Gerrit Web UI.
++
+This parameter only affects git over http traffic. If set to false
+then Gerrit will authenticate through DIGEST authentication and
+the randomly generated HTTP password in Gerrit DB.
++
+By default this is set to false.
+
 [[auth.userNameToLowerCase]]auth.userNameToLowerCase::
 +
 If set the username that is received to authenticate a git operation
@@ -354,8 +379,8 @@
 
 [[cache.name.maxAge]]cache.<name>.maxAge::
 +
-Maximum age to keep an entry in the cache.  If an entry has not
-been accessed in this period of time, it is removed from the cache.
+Maximum age to keep an entry in the cache. Entries are removed from
+the cache and refreshed from source data every maxAge interval.
 Values should use common unit suffixes to express their setting:
 +
 * s, sec, second, seconds
@@ -371,7 +396,7 @@
 supplied, the maximum age is infinite and items are never purged
 except when the cache is full.
 +
-Default is `90 days` for most caches, except:
+Default is `0`, meaning store forever with no expire, except:
 +
 * `"adv_bases"`: default is `10 minutes`
 * `"ldap_groups"`: default is `1 hour`
@@ -379,33 +404,42 @@
 
 [[cache.name.memoryLimit]]cache.<name>.memoryLimit::
 +
-Maximum number of cache items to retain in memory.  Keep in mind
-this is total number of items, not bytes of heap used.
+The total cost of entries to retain in memory. The cost computation
+varies by the cache. For most caches where the in-memory size of each
+entry is relatively the same, memoryLimit is currently defined to be
+the number of entries held by the cache (each entry costs 1).
++
+For caches where the size of an entry can vary significantly between
+individual entries (notably `"diff"`, `"diff_intraline"`), memoryLimit
+is an approximation of the total number of bytes stored by the cache.
+Larger entries that represent bigger patch sets or longer source files
+will consume a bigger portion of the memoryLimit. For these caches the
+memoryLimit should be set to roughly the amount of RAM (in bytes) the
+administrator can dedicate to the cache.
 +
 Default is 1024 for most caches, except:
 +
 * `"adv_bases"`: default is `4096`
-* `"diff"`: default is `128`
-* `"diff_intraline"`: default is `128`
+* `"diff"`: default is `10m` (10 MiB of memory)
+* `"diff_intraline"`: default is `10m` (10 MiB of memory)
+* `"plugin_resources"`: default is 2m (2 MiB of memory)
+
++
+If set to 0 the cache is disabled. Entries are removed immediately
+after being stored by the cache. This is primarily useful for testing.
 
 [[cache.name.diskLimit]]cache.<name>.diskLimit::
 +
-Maximum number of cache items to retain on disk, if this cache
-supports storing its items to disk.  Like memoryLimit, this is
-total number of items, not bytes of disk used.  If 0, disk storage
-for this cache is disabled.
+Total size in bytes of the keys and values stored on disk. Caches that
+have grown bigger than this size are scanned daily at 1 AM local
+server time to trim the cache. Entries are removed in least recently
+accessed order until the cache fits within this limit.  Caches may
+grow larger than this during the day, as the size check is only
+performed once every 24 hours.
 +
-Default is 16384.
-
-[[cache.name.diskBuffer]]cache.<name>.diskBuffer::
+Default is 128 MiB per cache.
 +
-Number of bytes to buffer in memory before writing less frequently
-accessed cache items to disk, if this cache supports storing its
-items to disk.
-+
-Default is 5 MiB.
-+
-Common unit suffixes of 'k', 'm', or 'g' are supported.
+If 0, disk storage for the cache is disabled.
 
 [[cache_names]]Standard Caches
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -447,14 +481,10 @@
 directory and file levels.  Gerrit uses this cache to accelerate
 the display of affected file names, as well as file contents.
 +
-Entries in this cache are relatively large, so the memory limit
-should not be set incredibly high.  Administrators should try to
-target cache.diff.memoryLimit to be roughly the number of changes
-which their users will process in a 1 or 2 day span.
-+
-Keeping entries for 90 days gives sufficient time for most changes
-to be submitted or abandoned before their relevant difference items
-expire out.
+Entries in this cache are relatively large, so memoryLimit is an
+estimate in bytes of memory used. Administrators should try to target
+cache.diff.memoryLimit to fit all changes users will view in a 1 or 2
+day span.
 
 cache `"diff_intraline"`::
 +
@@ -462,14 +492,10 @@
 between two commits. Gerrit uses this cache to accelerate display of
 intraline differences when viewing a file.
 +
-Entries in this cache are relatively large, so the memory limit
-should not be set incredibly high.  Administrators should try to
-target cache.diff.memoryLimit to be roughly the number of changes
-which their users will process in a 1 or 2 day span.
-+
-Keeping entries for 90 days gives sufficient time for most changes
-to be submitted or abandoned before their relevant difference items
-expire out.
+Entries in this cache are relatively large, so memoryLimit is an
+estimate in bytes of memory used. Administrators should try to target
+cache.diff.memoryLimit to fit all files users will view in a 1 or 2
+day span.
 
 cache `"git_tags"`::
 +
@@ -504,6 +530,10 @@
 low maxAge setting, to ensure LDAP modifications are picked up in
 a timely fashion.
 
+cache `"ldap_groups_byinclude"`::
++
+Caches the hierarchical structure of LDAP groups.
+
 cache `"ldap_usernames"`::
 +
 Caches a mapping of LDAP username to Gerrit account identity.  The
@@ -512,11 +542,17 @@
 
 cache `"permission_sort"`::
 +
-Caches the order access control sections must be applied to a
+Caches the order in which access control sections must be applied to a
 reference.  Sorting the sections can be expensive when regular
 expressions are used, so this cache remembers the ordering for
 each branch.
 
+cache `"plugin_resources"`::
++
+Caches formatted plugin resources, such as plugin documentation that
+has been converted from Markdown to HTML. The memoryLimit refers to
+the bytes of memory dedicated to storing the documentation.
+
 cache `"projects"`::
 +
 Caches the project description records, from the `projects` table
@@ -550,8 +586,8 @@
 unable to persist the session information.  Enabling a disk cache
 is strongly recommended.
 +
-Session storage is relatively inexpensive, the average entry in
-this cache is approximately 248 bytes, depending on the JVM.
+Session storage is relatively inexpensive. The average entry in
+this cache is approximately 346 bytes.
 
 See also link:cmd-flush-caches.html[gerrit flush-caches].
 
@@ -563,7 +599,7 @@
 Number of idle worker threads to maintain for the intraline difference
 computations.  There is no upper bound on how many concurrent requests
 can occur at once, if additional threads are started to handle a peak
-load, only this many will remaining idle afterwards.
+load, only this many will remain idle afterwards.
 +
 Default is 1.5x number of available CPUs.
 
@@ -639,7 +675,7 @@
 to changes which reference it.  The second configuration 'bugzilla'
 will hyperlink terms such as 'bug 42' to an external bug tracker,
 supplying the argument record number '42' for display.  The third
-configuration 'tracker' uses raw HTML to more preciously control
+configuration 'tracker' uses raw HTML to more precisely control
 how the replacement is displayed to the user.
 
 ----
@@ -980,6 +1016,10 @@
 
 ----
 [download]
+  command = checkout
+  command = cherry_pick
+  command = pull
+  command = format_patch
   scheme = ssh
   scheme = http
   scheme = anon_http
@@ -989,6 +1029,34 @@
 
 The download section configures the allowed download methods.
 
+[[download.command]]download.command::
++
+Commands that should be offered to download changes.
++
+Multiple commands are supported:
++
+* `checkout`
++
+Command to fetch and checkout the patch set.
++
+* `cherry_pick`
++
+Command to fetch the patch set and to cherry-pick it onto the current
+commit.
++
+* `pull`
++
+Command to pull the patch set.
++
+* `format_patch`
++
+Command to fetch the patch set and to feed it into the `format-patch`
+command.
+
++
+If `download.command` is not specified, all download commands are
+offered.
+
 [[download.scheme]]download.scheme::
 +
 Schemes that should be used to download changes.
@@ -1021,7 +1089,7 @@
 not default, as not all instances will deploy repo.
 
 +
-If download.scheme is not specified, SSH, HTTP and Anonymous HTTP
+If `download.scheme` is not specified, SSH, HTTP and Anonymous HTTP
 downloads are allowed.
 
 [[gerrit]]Section gerrit
@@ -1078,10 +1146,12 @@
 by the system administrator, and might not even be running on the
 same host as Gerrit.
 
-[[gerrit.replicateOnStartup]]gerrit.replicateOnStartup::
+[[gerrit.reportBugUrl]]gerrit.reportBugUrl::
 +
-If true, replicates to all remotes on startup to ensure they are
-in-sync with this server.  By default, true.
+URL to direct users to when they need to report a bug about the
+Gerrit service. By default this links to the upstream Gerrit
+Code Review's own bug tracker but could be directed to the system
+administrator's ticket queue.
 
 [[gitweb]]Section gitweb
 ~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1187,6 +1257,11 @@
 Optional filename for the patchset created hook, if not specified then
 `patchset-created` will be used.
 
+[[hooks.draftPublishedHook]]hooks.draftPublishedHook::
++
+Optional filename for the draft published hook, if not specified then
+`draft-published` will be used.
+
 [[hooks.commentAddedHook]]hooks.commentAddedHook::
 +
 Optional filename for the comment added hook, if not specified then
@@ -1202,6 +1277,21 @@
 Optional filename for the change abandoned hook, if not specified then
 `change-abandoned` will be used.
 
+[[hooks.changeRestoredHook]]hooks.changeRestoredHook::
++
+Optional filename for the change restored hook, if not specified then
+`change-restored` will be used.
+
+[[hooks.refUpdatedHook]]hooks.refUpdatedHook::
++
+Optional filename for the ref updated hook, if not specified then
+`ref-updated` will be used.
+
+[[hooks.claSignedHook]]hooks.claSignedHook::
++
+Optional filename for the CLA signed hook, if not specified then
+`cla-signed` will be used.
+
 [[http]]Section http
 ~~~~~~~~~~~~~~~~~~~~
 
@@ -1325,7 +1415,7 @@
 [[httpd.sslKeyPassword]]httpd.sslKeyPassword::
 +
 Password used to decrypt the private portion of the sslKeyStore.
-Java key stores require a password, even if the administrator
+Java keystores require a password, even if the administrator
 doesn't want to enable one.
 +
 If set to the empty string the embedded server will prompt for the
@@ -1345,7 +1435,7 @@
 [[httpd.acceptorThreads]]httpd.acceptorThreads::
 +
 Number of worker threads dedicated to accepting new incoming TCP
-connections and allocate them connection-specific resources.
+connections and allocating them connection-specific resources.
 +
 By default, 2, which should be suitable for most high-traffic sites.
 
@@ -1373,7 +1463,7 @@
 
 [[httpd.maxWait]]httpd.maxWait::
 +
-Maximum amount of time a client will wait to for an available
+Maximum amount of time a client will wait for an available
 thread to handle a project clone, fetch or push request over the
 smart HTTP transport.
 +
@@ -1398,7 +1488,7 @@
 [[ldap]]Section ldap
 ~~~~~~~~~~~~~~~~~~~~
 
-LDAP integration is only enabled if `auth.type` was set to
+LDAP integration is only enabled if `auth.type` is set to
 `HTTP_LDAP`, `LDAP` or `CLIENT_SSL_CERT_LDAP`.  See above for a
 detailed description of the auth.type settings and their
 implications.
@@ -1466,7 +1556,7 @@
 _(Optional)_ The read timeout for an LDAP operation. The value is
 in the usual time-unit format like "1 s", "100 ms", etc...
 A timeout can be used to avoid blocking all of the SSH command start
-threads in case when the LDAP server becomes slow.
+threads in case the LDAP server becomes slow.
 +
 By default there is no timeout and Gerrit will wait for the LDAP
 server to respond until the TCP connection times out.
@@ -1513,8 +1603,8 @@
 Typically this is the `displayName` property in LDAP, but could
 also be `legalName` or `cn`.
 +
-Attribute values may be concatenated with literal strings, for
-example to join given name and surname together use the pattern
+Attribute values may be concatenated with literal strings.  For
+example to join given name and surname together, use the pattern
 `${givenName} ${SN}`.
 +
 If set, users will be unable to modify their full name field, as
@@ -1535,7 +1625,7 @@
 `${sAMAccountName.toLowerCase}@example.com`.
 +
 If set, the preferred email address will be prefilled from LDAP,
-but users may still be able to register additional email address,
+but users may still be able to register additional email addresses,
 and select a different preferred email address.
 +
 Default is `mail`.
@@ -1625,7 +1715,7 @@
 +
 If set, it must be ensured that the local usernames for all existing
 accounts are converted to lower case, otherwise a user that has a
-local username that contains upper case characters cannot login
+local username that contains upper case characters will not be able to login
 anymore. The local usernames for the existing accounts can be
 converted to lower case by running the server program
 link:pgm-LocalUsernamesToLowerCase.html[LocalUsernamesToLowerCase].
@@ -1689,6 +1779,22 @@
 By default, 1.
 
 
+[[plugins]]Section plugins
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[[plugins.checkFrequency]]plugins.checkFrequency::
++
+How often plugins should be examined for new plugins to load, removed
+plugins to be unloaded, or updated plugins to be reloaded.  Values can
+be specified using standard time unit abbreviations ('ms', 'sec',
+'min', etc.).
++
+If set to 0, automatic plugin reloading is disabled.  Administrators
+may force reloading with link:cmd-plugin.html[gerrit plugin reload].
++
+Default is 1 minute.
+
+
 [[receive]]Section receive
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 This section is used to set who can execute the 'receive-pack' and
@@ -1719,7 +1825,7 @@
 and the push operation will fail. If set to zero then there is no
 limit.
 +
-Gerrit administrator can use this setting to prevent developers
+Gerrit administrators can use this setting to prevent developers
 from pushing objects which are too large to Gerrit.
 +
 Default is zero.
@@ -1870,6 +1976,22 @@
 +
 By default, unset, permitting delivery to any email address.
 
+[[sendemail.includeDiff]]sendemail.includeDiff::
++
+If true, new change emails from Gerrit will include the complete
+unified diff of the change. Variable maxmimumDiffSize places an upper
+limit on how large the email can get when this option is enabled.
++
+By default, false.
+
+[[sendemail.maximumDiffSize]]sendemail.maximumDiffSize::
++
+Largest size of unified diff output to include in an email. When
+the diff exceeds this size the file paths will be listed instead.
+Standard byte unit suffixes are supported.
++
+By default, 256 KiB.
+
 [[sendemail.importance]]sendemail.importance::
 +
 If present, emails sent from Gerrit will have the given level
@@ -1907,6 +2029,45 @@
 updated versions. If false, a server restart is required to change
 any of these resources. Default is true, allowing automatic reloads.
 
+[[site.enableDeprecatedQuery]]site.enableDeprecatedQuery::
++
+If true the deprecated `/query` URL is available to return JSON
+and text results for changes. If false, the URL is disabled and
+returns 404 to clients. Default is true, enabling `/query`.
+
+[[site.upgradeSchemaOnStartup]]site.upgradeSchemaOnStartup::
++
+Control whether schema upgrade should be done on Gerrit startup. The following
+values are supported:
++
+* `OFF`
++
+No automatic schema upgrade on startup.
++
+* `AUTO`
++
+Perform schema migration on startup, if necessary.  If, as a result of
+schema migration, there would be any unused database objects they will
+be dropped automatically.
++
+* `AUTO_NO_PRUNE`
++
+Like `AUTO` but unused database objects will not be pruned.
+
++
+The default is `OFF`.
+
+[[ssh-alias]] Section ssh-alias
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Variables in section ssh-alias permit the site administrator to alias
+another command from Gerrit or a plugin into the `gerrit` command
+namespace. To alias `replication start` to `gerrit replicate`:
+
+----
+[ssh-alias]
+  replicate = replication start
+----
 
 [[sshd]] Section sshd
 ~~~~~~~~~~~~~~~~~~~~~
@@ -1969,7 +2130,7 @@
 +
 Number of threads to use when executing SSH command requests.
 If additional requests are received while all threads are busy they
-are queued and serviced in a first-come-first-serve order.
+are queued and serviced in a first-come-first-served order.
 +
 By default, 1.5x the number of CPUs available to the JVM.
 
@@ -2036,7 +2197,7 @@
 +
 Maximum number of concurrent SSH sessions that a user account
 may open at one time.  This is the number of distinct SSH logins
-the each user may have active at one time, and is not related to
+that each user may have active at one time, and is not related to
 the number of commands a user may issue over a single connection.
 If set to 0, there is no limit.
 +
@@ -2132,6 +2293,31 @@
 +
 By default a shade of yellow, `FFFFCC`.
 
+[[theme.changeTableOutdatedColor]]theme.changeTableOutdatedColor::
++
+Background color used for patch outdated messages.  The value must be
+a valid HTML hex color code, or standard color name.
++
+By default a shade of red, `F08080`.
+
+[[theme.tableOddRowColor]]theme.tableOddRowColor::
++
+Background color for tables such as lists of open reviews for odd
+rows.  This is so you can have a different color for odd and even
+rows of the table.  The value must be a valid HTML hex color code,
+or standard color name.
++
+By default transparent.
+
+[[theme.tableEvenRowColor]]theme.tableEvenRowColor::
++
+Background color for tables such as lists of open reviews for even
+rows.  This is so you can have a different color for odd and even
+rows of the table.  The value must be a valid HTML hex color code,
+or standard color name.
++
+By default transparent.
+
 A different theme may be used for signed-in vs. signed-out user status
 by using the "signed-in" and "signed-out" theme sections. Variables
 not specified in a section are inherited from the default theme.
@@ -2184,7 +2370,7 @@
 external tracking id part of the footer line. The match can
 result in several entries in the DB.  If grouping is used in the
 regex the first group will be interpreted as the tracking id.
-Tracking ids > 20 char will be ignored.
+Tracking ids longer than 20 characters will be ignored.
 +
 The configuration file parser eats one level of backslashes, so the
 character class `\s` requires `\\s` in the configuration file.  The
@@ -2193,7 +2379,7 @@
 
 [[trackingid.name.system]]trackingid.<name>.system::
 +
-The name of the external tracking system(max 10 char).
+The name of the external tracking system (maximum 10 characters).
 It is possible to have several trackingid entries for the same
 tracking system.
 
@@ -2276,6 +2462,7 @@
 ----
 [auth]
   registerEmailPrivateKey = 2zHNrXE2bsoylzUqDxZp0H1cqUmjgWb6
+  restTokenPrivateKey = 7e40PzCjlUKOnXATvcBNXH6oyiu+r0dFk2c=
 
 [database]
   username = webuser
@@ -2294,15 +2481,6 @@
   password = s3kr3t
 ----
 
-File `etc/replication.config`
------------------------------
-
-The optional file `'$site_path'/etc/replication.config` controls how
-Gerrit automatically replicates changes it makes to any of the Git
-repositories under its control.
-
-* link:config-replication.html[Git Replication/Mirroring]
-
 File `etc/peer_keys`
 --------------------
 
@@ -2338,7 +2516,6 @@
 Other files support site customization.
 +
 * link:config-headerfooter.html[Site Header/Footer]
-* link:config-replication.html[Git Replication/Mirroring]
 
 GERRIT
 ------
diff --git a/Documentation/config-gitweb.txt b/Documentation/config-gitweb.txt
index a08eb87..35d5c0d 100644
--- a/Documentation/config-gitweb.txt
+++ b/Documentation/config-gitweb.txt
@@ -27,7 +27,7 @@
 Alternatively, if Gerrit is served behind reverse proxy, it can
 generate different URLs for gitweb's links (they need to be
 rewritten to `<gerrit>/gitweb?args` on the web server). This allows
-for serving gitweb under different URL than the Gerrit instance.
+for serving gitweb under a different URL than the Gerrit instance.
 To enable this feature, set both: `gitweb.cgi` and `gitweb.url`.
 
 ====
diff --git a/Documentation/config-headerfooter.txt b/Documentation/config-headerfooter.txt
index c06080b..ae5d8f7 100644
--- a/Documentation/config-headerfooter.txt
+++ b/Documentation/config-headerfooter.txt
@@ -42,7 +42,7 @@
 or `GerritSite.css` by the relative URL `static/$name`
 (e.g. `static/logo.png`).
 
-To simplify security management, only files are served from
+To simplify security management, files are only served from
 `'$site_path'/static`.  Subdirectories are explicitly forbidden from
 being served from this location by enforcing the rule that file names
 cannot contain `/` or `\`.  (Client requests for `static/foo/bar`
diff --git a/Documentation/config-hooks.txt b/Documentation/config-hooks.txt
index ceb7c78..dfdba52 100644
--- a/Documentation/config-hooks.txt
+++ b/Documentation/config-hooks.txt
@@ -24,10 +24,19 @@
 ~~~~~~~~~~~~~~~~
 
 This is called whenever a patchset is created (this includes new
-changes)
+changes and drafts).
 
 ====
-  patchset-created --change <change id> --change-url <change url> --project <project name> --branch <branch> --uploader <uploader> --commit <sha1> --patchset <patchset id>
+  patchset-created --change <change id> --is-draft <boolean> --change-url <change url> --project <project name> --branch <branch> --topic <topic> --uploader <uploader> --commit <sha1> --patchset <patchset id>
+====
+
+draft-published
+~~~~~~~~~~~~~~~
+
+This is called whenever a draft change is published.
+
+====
+  draft-published --change <change id> --change-url <change url> --project <project name> --branch <branch> --topic <topic> --uploader <uploader> --commit <sha1> --patchset <patchset id>
 ====
 
 comment-added
@@ -36,7 +45,7 @@
 This is called whenever a comment is added to a change.
 
 ====
-  comment-added --change <change id> --change-url <change url> --project <project name> --branch <branch> --author <comment author> --commit <commit> --comment <comment> [--<approval category id> <score> --<approval category id> <score> ...]
+  comment-added --change <change id> --change-url <change url> --project <project name> --branch <branch> --topic <topic> --author <comment author> --commit <commit> --comment <comment> [--<approval category id> <score> --<approval category id> <score> ...]
 ====
 
 change-merged
@@ -45,7 +54,7 @@
 Called whenever a change has been merged.
 
 ====
-  change-merged --change <change id> --change-url <change url> --project <project name> --branch <branch> --submitter <submitter> --commit <sha1>
+  change-merged --change <change id> --change-url <change url> --project <project name> --branch <branch> --topic <topic> --submitter <submitter> --commit <sha1>
 ====
 
 change-abandoned
@@ -54,16 +63,16 @@
 Called whenever a change has been abandoned.
 
 ====
-  change-abandoned --change <change id> --change-url <change url> --project <project name> --branch <branch> --abandoner <abandoner> --reason <reason>
+  change-abandoned --change <change id> --change-url <change url> --project <project name> --branch <branch> --topic <topic> --abandoner <abandoner> --reason <reason>
 ====
 
 change-restored
-~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~
 
 Called whenever a change has been restored.
 
 ====
-  change-restored --change <change id> --change-url <change url> --project <project name> --branch <branch> --restorer <restorer> --reason <reason>
+  change-restored --change <change id> --change-url <change url> --project <project name> --branch <branch> --topic <topic> --restorer <restorer> --reason <reason>
 ====
 
 ref-updated
@@ -76,9 +85,9 @@
 ====
 
 cla-signed
-~~~~~~~~~~~
+~~~~~~~~~~
 
-Called whenever a user signs a contributor license agreement
+Called whenever a user signs a contributor license agreement.
 
 ====
   cla-signed --submitter <submitter> --user-id <user_id> --cla-id <cla_id>
@@ -88,13 +97,15 @@
 Configuration Settings
 ----------------------
 
-It is possible to change where gerrit looks for hooks, and what
-filenames it looks for by adding a [hooks] section to gerrit.config.
+It is possible to change where Gerrit looks for hooks, and what
+filenames it looks for, by adding a [hooks] section in gerrit.config.
 
-Gerrit will use the value of hooks.path for the hooks directory, and
-the values of hooks.patchsetCreatedHook, hooks.commentAddedHook,
-hooks.changeMergedHook and hooks.changeAbandonedHook for the
-filenames for the hooks.
+Gerrit will use the value of hooks.path for the hooks directory.
+
+For the hook filenames, Gerrit will use the values of hooks.patchsetCreatedHook,
+hooks.draftPublishedHook, hooks.commentAddedHook, hooks.changeMergedHook,
+hooks.changeAbandonedHook, hooks.changeRestoredHook, hooks.refUpdatedHook and
+hooks.claSignedHook.
 
 Missing Change URLs
 -------------------
diff --git a/Documentation/config-mail.txt b/Documentation/config-mail.txt
index 8aa7d08..ad0704f 100644
--- a/Documentation/config-mail.txt
+++ b/Documentation/config-mail.txt
@@ -20,7 +20,7 @@
 Supported Mail Templates:
 -------------------------
 
-Each mail that Gerrit sends out is controlled by at least one template, these
+Each mail that Gerrit sends out is controlled by at least one template.  These
 are listed below.  Change emails are influenced by two additional templates,
 one to set the subject line, and one to set the footer which gets appended to
 all the change emails (see `ChangeSubject.vm` and `ChangeFooter.vm` below.)
@@ -36,7 +36,7 @@
 ~~~~~~~~~~~~~~~
 
 The `ChangeFooter.vm` template will determine the contents of the footer
-text that will be appended to emails related to changes (all `ChangeEmails)`.
+text that will be appended to emails related to changes (all `ChangeEmail`s).
 
 ChangeSubject.vm
 ~~~~~~~~~~~~~~~~
@@ -49,6 +49,7 @@
 
 The `Comment.vm` template will determine the contents of the email related to
 a user submitting comments on changes.  It is a `ChangeEmail`: see
+`ChangeSubject.vm` and `ChangeFooter.vm`.
 
 Merged.vm
 ~~~~~~~~~
@@ -62,6 +63,7 @@
 
 The `MergeFail.vm` template will determine the contents of the email related
 to a failure upon attempting to merge a change to the head.  It is a
+`ChangeEmail`: see `ChangeSubject.vm` and `ChangeFooter.vm`.
 
 NewChange.vm
 ~~~~~~~~~~~~
@@ -70,6 +72,13 @@
 to a user submitting a new change for review. It is a `ChangeEmail`: see
 `ChangeSubject.vm` and `ChangeFooter.vm`.
 
+RebasedPatchSet.vm
+~~~~~~~~~~~~~~~~~~
+
+The `RebasedPatchSet.vm` template will determine the contents of the email
+related to a user rebasing a patchset for a change through the Gerrit UI.
+It is a `ChangeEmail`: see `ChangeSubject.vm` and `ChangeFooter.vm`.
+
 RegisterNewEmail.vm
 ~~~~~~~~~~~~~~~~~~~
 
@@ -90,6 +99,12 @@
 to a change being restored.  It is a `ChangeEmail`: see `ChangeSubject.vm` and
 `ChangeFooter.vm`.
 
+Reverted.vm
+~~~~~~~~~~~
+
+The `Reverted.vm` template will determine the contents of the email related
+to a change being reverted.  It is a `ChangeEmail`: see `ChangeSubject.vm` and
+`ChangeFooter.vm`.
 
 
 Mail Variables and Methods
@@ -107,7 +122,7 @@
 Warning
 ~~~~~~~
 
-Be aware that modifying templates can cause them to fail to parse and therefor
+Be aware that modifying templates can cause them to fail to parse and therefore
 not send out the actual email, or worse, calling methods on the available
 objects could have internal side effects which would adversely affect the
 health of your Gerrit server and/or data.
@@ -125,7 +140,7 @@
 
 $messageClass::
 +
-A String containing the messageClass
+A String containing the messageClass.
 
 $StringUtils::
 +
@@ -139,35 +154,35 @@
 
 $change::
 +
-A reference to the current `Change` object
+A reference to the current `Change` object.
 
 $changeId::
 +
-Id of the current change (a `Change.Key`)
+Id of the current change (a `Change.Key`).
 
 $coverLetter::
 +
-The text of the `ChangeMessage`
+The text of the `ChangeMessage`.
 
 $branch::
 +
-A reference to the branch of this change (a `Branch.NameKey`)
+A reference to the branch of this change (a `Branch.NameKey`).
 
 $fromName::
 +
-The name of the from user
+The name of the from user.
 
 $projectName::
 +
-The name of this change's project
+The name of this change's project.
 
 $patchSet::
 +
-A reference to the current `PatchSet`
+A reference to the current `PatchSet`.
 
 $patchSetInfo::
 +
-A reference to the current `PatchSetInfo`
+A reference to the current `PatchSetInfo`.
 
 
 See Also
diff --git a/Documentation/config-replication.txt b/Documentation/config-replication.txt
deleted file mode 100644
index 772282e..0000000
--- a/Documentation/config-replication.txt
+++ /dev/null
@@ -1,273 +0,0 @@
-Gerrit Code Review - Git Replication
-====================================
-
-Gerrit can automatically push any changes it makes to its managed Git
-repositories to another system.  Usually this would be configured to
-provide mirroring of changes, for warm-standby backups, or a
-load-balanced public mirror farm.
-
-The replication runs on a short delay.  This gives Gerrit a small
-time window to batch updates going to the same project, such as
-when a user uploads multiple changes at once.
-
-Typically replication should be done over SSH, with a passwordless
-public/private key pair.  On a trusted network it is also possible to
-use replication over the insecure (but much faster) git:// protocol,
-by enabling the `receive-pack` service on the receiving system, but
-this configuration is not recommended.  It is also possible to
-specify a local path as replication target. This makes e.g. sense if
-a network share is mounted to which the repositories should be
-replicated.
-
-Enabling Replication
---------------------
-
-If replicating over SSH (recommended), ensure the host key of the
-remote system(s) is already in the Gerrit user's `~/.ssh/known_hosts`
-file.  The easiest way to add the host key is to connect once by hand
-with the command line:
-
-====
-  sudo su -c 'ssh mirror1.us.some.org echo' gerrit2
-====
-
-Next, create `'$site_path'/etc/replication.config` as a Git-style
-config file, and restart Gerrit.
-
-Example `replication.config` to replicate in parallel to four
-different hosts:
-
-====
-  [remote "host-one"]
-    url = gerrit2@host-one.example.com:/some/path/${name}.git
-
-  [remote "pubmirror"]
-    url = mirror1.us.some.org:/pub/git/${name}.git
-    url = mirror2.us.some.org:/pub/git/${name}.git
-    url = mirror3.us.some.org:/pub/git/${name}.git
-    push = +refs/heads/*:refs/heads/*
-    push = +refs/tags/*:refs/tags/*
-    threads = 3
-    authGroup = Public Mirror Group
-    authGroup = Second Public Mirror Group
-====
-
-To manually trigger replication at runtime, see
-link:cmd-replicate.html[gerrit replicate].
-
-[[replication_config]]File `replication.config`
------------------------------------------------
-
-The optional file `'$site_path'/etc/replication.config` is a
-Git-style config file that controls the replication settings for
-Gerrit.
-
-The file is composed of one or more `remote` sections, each remote
-section provides common configuration settings for one or more
-destination URLs.
-
-Each remote section uses its own thread pool.  If pushing to
-multiple remotes, over differing types of network connections
-(e.g. LAN and also public Internet), its a good idea to put them
-into different remote sections, so that replication to the slower
-connection does not starve out the faster local one.  The example
-file above does this.
-
-[[remote]]Section remote
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-In the keys below, the <name> portion is unused by Gerrit, but must be
-unique to distinguish the different sections if more than one remote
-section appears in the file.
-
-[[remote.name.url]]remote.<name>.url::
-+
-Address of the remote server to push to.  Multiple URLs may
-be specified within a single remote block, listing different
-destinations which share the same settings.  Assuming sufficient
-threads in the thread pool, Gerrit pushes to all URLs in parallel,
-using one thread per URL.
-+
-Within each URL value the magic placeholder `${name}` is replaced
-with the Gerrit project name.  This is a Gerrit specific extension
-to the otherwise standard Git URL syntax and it must be included
-in each URL so that Gerrit can figure out where each project needs
-to be replicated.
-+
-See link:http://www.kernel.org/pub/software/scm/git/docs/git-push.html#URLS[GIT URLS]
-for details on Git URL syntax.
-
-[[remote.name.url]]remote.<name>.adminUrl::
-+
-Address of the alternative remote server only for repository creation.  Multiple URLs may
-be specified within a single remote block, listing different
-destinations which share the same settings.
-+
-The adminUrl can be used as a ssh alternative to the url option, but only related to repository creation.
-If not specified, the repository creation tries to follow the default way through the url value specified.
-+
-It is useful when remote.<name>.url protocols does not allow repository creation
-although their usage are mandatory in the local environment.
-In that case, an alternative ssh url could be specified to repository creation.
-
-[[remote.name.receivepack]]remote.<name>.receivepack::
-+
-Path of the `git-receive-pack` executable on the remote system, if
-using the SSH transport.
-+
-Defaults to `git-receive-pack`.
-
-[[remote.name.uploadpack]]remote.<name>.uploadpack::
-+
-Path of the `git-upload-pack` executable on the remote system, if
-using the SSH transport.
-+
-Defaults to `git-upload-pack`.
-
-[[remote.name.push]]remote.<name>.push::
-+
-Standard Git refspec denoting what should be replicated.  Setting this
-to `+refs/heads/*:refs/heads/*` would mirror only the active
-branches, but not the change refs under `refs/changes/`, or the tags
-under `refs/tags/`.
-+
-Multiple push keys can be supplied, to specify multiple patterns
-to match against.  In the example file above, remote "pubmirror"
-uses two push keys to match both `refs/heads/*` and `refs/tags/*`,
-but excludes all others, including `refs/changes/*`.
-+
-Defaults to `+refs/*:refs/*` (all refs) if not specified.
-
-[[remote.name.timeout]]remote.<name>.timeout::
-+
-Number of seconds to wait for a network read or write to complete
-before giving up and declaring the remote side is not responding.
-If 0, there is no timeout, and the push client waits indefinitely.
-+
-A timeout should be large enough to mostly transfer the objects to
-the other side.  1 second may be too small for larger projects,
-especially over a WAN link, while 10-30 seconds is a much more
-reasonable timeout value.
-+
-Defaults to 0 seconds, wait indefinitely.
-
-[[remote.name.replicationDelay]]remote.<name>.replicationDelay::
-+
-Number of seconds to wait before scheduling a remote push operation.
-Setting the delay to 0 effectively disables the delay, causing the
-push to start as soon as possible.
-+
-This is a Gerrit specific extension to the Git remote block.
-+
-By default, 15 seconds.
-
-[[remote.name.replicationRetry]]remote.<name>.replicationRetry::
-+
-Number of minutes to wait before scheduling a remote push operation
-previously failed due to an offline remote server.
-+
-If a remote push operation fails because a remote server was
-offline, all push operations to the same destination URL are
-blocked, and the remote push is continuously retried.
-+
-This is a Gerrit specific extension to the Git remote block.
-+
-By default, 1 minute.
-
-[[remote.name.threads]]remote.<name>.threads::
-+
-Number of worker threads to dedicate to pushing to the repositories
-described by this remote.  Each thread can push one project at a
-time, to one destination URL.  Scheduling within the thread pool
-is done on a per-project basis.  If a remote block describes 4
-URLs, allocating 4 threads in the pool will permit some level of
-parallel pushing.
-+
-By default, 1 thread.
-
-[[remote.name.authGroup]]remote.<name>.authGroup::
-+
-Specifies the name of a group that the remote should use to access
-the repositories. Multiple authGroups may be specified within a
-single remote block to signify a wider access right. In the project
-administration web interface the read access can be specified for
-this group to control if a project should be replicated or not to the
-remote.
-+
-By default, replicates without group control, i.e replicates
-everything to all remotes.
-
-[[remote.name.replicatePermissions]]remote.<name>.replicatePermissions::
-+
-If true, permissions-only projects and the refs/meta/config branch
-will also be replicated to the remote site.  These projects and
-branches may be needed to keep a backup or slave server current.
-+
-By default, true, replicating everything.
-
-[[remote.name.mirror]]remote.<name>.mirror::
-+
-If true, replication will remove remote branches that absent locally
-or invisible to the replication (i.e. read access denied via 'authGroup'
-option).
-+
-By default, false, do not remove remote branches.
-
-
-[[secure_config]]File `secure.config`
------------------------------------------------
-
-The optional file `'$site_path'/secure.config` is a Git-style config
-file that provides secure values that should not be world-readable,
-such as passwords. Passwords for HTTP remotes can be obtained from
-this file.
-
-[[remote.name.username]]remote.<name>.username::
-+
-Username to use for HTTP authentication on this remote, if not given
-in the URL.
-
-[[remote.name.password]]remote.<name>.password::
-+
-Password to use for HTTP authentication on this remote.
-
-
-[[ssh_config]]File `~/.ssh/config`
-----------------------------------
-
-If present, Gerrit reads and caches `~/.ssh/config` at startup, and
-supports most SSH configuration options.  For example:
-
-====
-  Host host-one.example.com:
-    IdentityFile ~/.ssh/id_hostone
-    PreferredAuthentications publickey
-
-  Host mirror*.us.some.org:
-    User mirror-updater
-    IdentityFile ~/.ssh/id_pubmirror
-    PreferredAuthentications publickey
-====
-
-Supported options:
-
- * Host
- * Hostname
- * User
- * Port
- * IdentityFile
- * PreferredAuthentications
- * StrictHostKeyChecking
-
-SSH authentication must be by passwordless public key, as there is
-no facility to read passphases on startup or passwords during the
-SSH connection setup, and SSH agents are not supported from Java.
-
-Host keys for any destination SSH servers must appear in the user's
-`~/.ssh/known_hosts` file, and must be added in advance, before
-Gerrit starts.  If a host key is not listed, Gerrit will be unable to
-connect to that destination, and replication to that URL will fail.
-
-GERRIT
-------
-Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/config-reverseproxy.txt b/Documentation/config-reverseproxy.txt
index 4eecf67..7161c4a 100644
--- a/Documentation/config-reverseproxy.txt
+++ b/Documentation/config-reverseproxy.txt
@@ -5,7 +5,7 @@
 -----------
 
 Gerrit can be configured to run behind a third-party web server.
-This allows the other web server to bind to the privileged ports 80
+This allows the other web server to bind to the privileged port 80
 (or 443 for SSL), as well as offloads the SSL processing overhead
 from Java to optimized native C code.
 
diff --git a/Documentation/config-sso.txt b/Documentation/config-sso.txt
index 9aa06be..e915ffb 100644
--- a/Documentation/config-sso.txt
+++ b/Documentation/config-sso.txt
@@ -146,7 +146,7 @@
 The auth.type must always be HTTP, indicating the user identity
 will be obtained from the HTTP authorization data.
 
-The auth.httpHeader indicates which HTTP header field the Siteminder
+The auth.httpHeader indicates in which HTTP header field the Siteminder
 product has stored the username.  Usually this is "SM_USER", but
 may differ in your environment.  Please refer to your organization's
 single sign-on or security group to ensure the setting is correct.
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt
index 2609b05..065e9d1 100644
--- a/Documentation/dev-contributing.txt
+++ b/Documentation/dev-contributing.txt
@@ -150,7 +150,7 @@
     should be before the instance members.
   * Annotations should go before language keywords (final, private...) +
     Example: @Assisted @Nullable final type varName
-  * Imports should be mostly aphabetical (uppercase sorts before
+  * Imports should be mostly alphabetical (uppercase sorts before
     all lowercase, which means classes come before packages at the
     same level).
 
@@ -164,7 +164,7 @@
 Design
 ------
 
-Here are some design level ojectives that you should keep in mind
+Here are some design level objectives that you should keep in mind
 when coding:
 
   * ORM entity objects should match exactly one row in the database.
@@ -191,6 +191,7 @@
     on slow links.  If the action buttons are disabled, they cannot
     be resubmitted and the user can see that Gerrit is still busy.
   * GWT EventBus is the new way forward.
+  * ...and so is Guava (previously known as Google Collections).
 
 
 Tests
diff --git a/Documentation/dev-design.txt b/Documentation/dev-design.txt
index bf5ba73..ce2868c 100644
--- a/Documentation/dev-design.txt
+++ b/Documentation/dev-design.txt
@@ -37,7 +37,7 @@
 
 Git is a distributed version control system, wherein each repository
 is assumed to be owned/maintained by a single user.  There are no
-inherit security controls built into Git, so the ability to read
+inherent security controls built into Git, so the ability to read
 from or write to a repository is controlled entirely by the host's
 filesystem access controls.  When multiple maintainers collaborate
 on a single shared repository a high degree of trust is required,
@@ -87,7 +87,7 @@
 
 Each Git commit created on the client desktop system is converted
 into a unique change record which can be reviewed independently.
-Change records are stored in a database: PostgreSQL, MySql, or the
+Change records are stored in a database: PostgreSQL, MySQL, or the
 built-in H2, where they can be queried to present customized user
 dashboards, enumerating any pending changes.
 
@@ -191,7 +191,7 @@
 
 * link:http://code.google.com/p/gerrit/[Project Homepage]
 * link:http://code.google.com/p/gerrit/downloads/list[Release Versions]
-* link:http://code.google.com/p/gerrit/wiki/Source?tm=4[Source]
+* link:http://code.google.com/p/gerrit/source/checkout[Source]
 * link:http://code.google.com/p/gerrit/issues/list[Issue Tracking]
 * link:https://review.source.android.com/[Change Review]
 
@@ -669,17 +669,18 @@
 Backups
 ~~~~~~~
 
-PostgreSQL can be configured to save its write-ahead-log (WAL)
-and ship these logs to other systems, where they are applied to
-a warm-standby backup in real time.  Gerrit instances which care
-about reduduncy will setup this feature of PostgreSQL to ensure
-the warm-standby is reasonably current should the master go offline.
+PostgreSQL and MySQL can be configured to replicate their data to
+other systems, where they are applied to a warm-standby backup in
+real time.  Gerrit instances which care about reduduncy will setup
+this feature of PostgreSQL or MySQL to ensure the warm-standby is
+reasonably current should the master go offline.
 
-Gerrit can be configured to replicate changes made to the local
-Git repositories over any standard Git transports.  This can be
-configured in `'$site_path'/etc/replication.conf` to send copies
-of all changes over SSH to other servers, or to the Amazon S3 blob
-storage service.
+Using the standard replication plugin, Gerrit can be configured
+to replicate changes made to the local Git repositories over any
+standard Git transports. After the plugin is installed, remote
+destinations can be configured in `'$site_path'/etc/replication.conf`
+to send copies of all changes over SSH to other servers, or to the
+Amazon S3 blob storage service.
 
 
 Logging Plan
diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt
index e239a63..b2bf011 100644
--- a/Documentation/dev-eclipse.txt
+++ b/Documentation/dev-eclipse.txt
@@ -70,12 +70,19 @@
 * Change Save as to be Local file.
 
 
+[[hosted-mode]]
 Running Hosted Mode
 ~~~~~~~~~~~~~~~~~~~
 
-Import the gerrit-gwtdebug project:
+To debug the GWT code executing in the web browser, three additional Git
+repositories need to be cloned.
 
-* Import gerrit-gwtdebug/pom.xml using General -> Maven Projects
+* https://gerrit.googlesource.com/gwtexpui
+* https://gerrit.googlesource.com/gwtjsonrpc
+* https://gerrit.googlesource.com/gwtorm
+
+In Eclipse, import the pom.xml file in the root directory of each of
+these cloned gits via General -> Maven Projects.
 
 Duplicate the existing `gwtui_dbg` launch configuration:
 
@@ -94,6 +101,22 @@
 * Change Save as to be Local file.
 
 
+[[known-problems]]
+Known problems
+--------------
+
+* When running Gerrit under the Eclipse debugger, code that attempts
+to load Prolog code may erroneously raise ClassNotFoundException,
+claiming that classes in the `Gerrit` package can't be found. The
+error can often be resolved by rebuilding Gerrit with `mvn package`
+and restarting the debug session.
+
+* OpenID authentication won't work in hosted mode, so you need to change
+the link:config-gerrit.html#auth.type[auth.type] configuration parameter
+to `DEVELOPMENT_BECOME_ANY_ACCOUNT` to disable OpenID and allow you to
+impersonate whatever account you otherwise would've used.
+
+
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
new file mode 100644
index 0000000..dd0c44c
--- /dev/null
+++ b/Documentation/dev-plugins.txt
@@ -0,0 +1,420 @@
+Gerrit Code Review - Plugin Development
+=======================================
+
+The Gerrit server functionality can be extended by installing plugins.
+This page describes how plugins for Gerrit can be developed.
+
+Depending on how tightly the extension code is coupled with the Gerrit
+server code, there is a distinction between `plugins` and `extensions`.
+
+[[plugin]]
+A `plugin` in Gerrit is tightly coupled code that runs in the same
+JVM as Gerrit. It has full access to all server internals. Plugins
+are tightly coupled to a specific major.minor server version and
+may require source code changes to compile against a different
+server version.
+
+[[extension]]
+An `extension` in Gerrit runs inside of the same JVM as Gerrit
+in the same way as a plugin, but has limited visibility to the
+server's internals. The limited visibility reduces the extension's
+dependencies, enabling it to be compatible across a wider range
+of server versions.
+
+Most of this documentation refers to either type as a plugin.
+
+[[getting-started]]
+Getting started
+---------------
+
+To get started with the development of a plugin there are two
+recommended ways:
+
+. use the Gerrit Plugin Maven archetype to create a new plugin project:
++
+With the Gerrit Plugin Maven archetype you can create a skeleton for a
+plugin project.
++
+----
+mvn archetype:generate -DarchetypeGroupId=com.google.gerrit \
+    -DarchetypeArtifactId=gerrit-plugin-archetype \
+    -DarchetypeVersion=2.5-SNAPSHOT \
+    -DgroupId=com.google.gerrit \
+    -DartifactId=testPlugin
+----
++
+Maven will ask for additional properties and then create the plugin in
+the current directory. To change the default property values answer 'n'
+when Maven asks to confirm the properties configuration. It will then
+ask again for all properties including those with predefined default
+values.
+
+. clone the sample helloworld plugin:
++
+This is a Maven project that adds an SSH command to Gerrit to print
+out a hello world message. It can be taken as an example to develop
+an own plugin.
++
+----
+$ git clone https://gerrit.googlesource.com/plugins/helloworld
+----
++
+When starting from this example one should take care to adapt the
+`Gerrit-ApiVersion` in the `pom.xml` to the version of Gerrit for which
+the plugin is developed. If the plugin is developed for a released
+Gerrit version (no `SNAPSHOT` version) then the URL for the
+`gerrit-api-repository` in the `pom.xml` needs to be changed to
+`https://gerrit-api.commondatastorage.googleapis.com/release/`.
+
+[[API]]
+API
+---
+
+There are two different API formats offered against which plugins can
+be developed:
+
+gerrit-extension-api.jar::
+  A stable but thin interface. Suitable for extensions that need
+  to be notified of events, but do not require tight coupling to
+  the internals of Gerrit. Extensions built against this API can
+  expect to be binary compatible across a wide range of server
+  versions.
+
+gerrit-plugin-api.jar::
+  The complete internals of the Gerrit server, permitting a
+  plugin to tightly couple itself and provide additional
+  functionality that is not possible as an extension. Plugins
+  built against this API are expected to break at the source
+  code level between every major.minor Gerrit release. A plugin
+  that compiles against 2.5 will probably need source code level
+  changes to work with 2.6, 2.7, and so on.
+
+Manifest
+--------
+
+Plugins may provide optional description information with standard
+manifest fields:
+
+====
+  Implementation-Title: Example plugin showing examples
+  Implementation-Version: 1.0
+  Implementation-Vendor: Example, Inc.
+  Implementation-URL: http://example.com/opensource/plugin-foo/
+====
+
+ApiType
+~~~~~~~
+
+Plugins using the tightly coupled `gerrit-plugin-api.jar` must
+declare this API dependency in the manifest to gain access to server
+internals. If no `Gerrit-ApiType` is specified the stable `extension`
+API will be assumed. This may cause ClassNotFoundExceptions when
+loading a plugin that needs the plugin API.
+
+====
+  Gerrit-ApiType: plugin
+====
+
+Explicit Registration
+~~~~~~~~~~~~~~~~~~~~~
+
+Plugins that use explicit Guice registration must name the Guice
+modules in the manifest. Up to three modules can be named in the
+manifest. `Gerrit-Module` supplies bindings to the core server;
+`Gerrit-SshModule` supplies SSH commands to the SSH server (if
+enabled); `Gerrit-HttpModule` supplies servlets and filters to the HTTP
+server (if enabled). If no modules are named automatic registration
+will be performed by scanning all classes in the plugin JAR for
+`@Listen` and `@Export("")` annotations.
+
+====
+  Gerrit-Module:     tld.example.project.CoreModuleClassName
+  Gerrit-SshModule:  tld.example.project.SshModuleClassName
+  Gerrit-HttpModule: tld.example.project.HttpModuleClassName
+====
+
+[[reload_method]]
+Reload Method
+~~~~~~~~~~~~~
+
+If a plugin holds an exclusive resource that must be released before
+loading the plugin again (for example listening on a network port or
+acquiring a file lock) the manifest must declare `Gerrit-ReloadMode`
+to be `restart`. Otherwise the preferred method of `reload` will
+be used, as it enables the server to hot-patch an updated plugin
+with no down time.
+
+====
+  Gerrit-ReloadMode: restart
+====
+
+In either mode ('restart' or 'reload') any plugin or extension can
+be updated without restarting the Gerrit server. The difference is
+how Gerrit handles the upgrade:
+
+restart::
+  The old plugin is completely stopped. All registrations of SSH
+  commands and HTTP servlets are removed. All registrations of any
+  extension points are removed. All registered LifecycleListeners
+  have their `stop()` method invoked in reverse order. The new
+  plugin is started, and registrations are made from the new
+  plugin. There is a brief window where neither the old nor the
+  new plugin is connected to the server. This means SSH commands
+  and HTTP servlets will return not found errors, and the plugin
+  will not be notified of events that occurred during the restart.
+
+reload::
+  The new plugin is started. Its LifecycleListeners are permitted
+  to perform their `start()` methods. All SSH and HTTP registrations
+  are atomically swapped out from the old plugin to the new plugin,
+  ensuring the server never returns a not found error. All extension
+  point listeners are atomically swapped out from the old plugin to
+  the new plugin, ensuring no events are missed (however some events
+  may still route to the old plugin if the swap wasn't complete yet).
+  The old plugin is stopped.
+
+To reload/restart a plugin the link:cmd-plugin-reload.html[plugin reload]
+command can be used.
+
+[[classpath]]
+Classpath
+---------
+
+Each plugin is loaded into its own ClassLoader, isolating plugins
+from each other. A plugin or extension inherits the Java runtime
+and the Gerrit API chosen by `Gerrit-ApiType` (extension or plugin)
+from the hosting server.
+
+Plugins are loaded from a single JAR file. If a plugin needs
+additional libraries, it must include those dependencies within
+its own JAR. Plugins built using Maven may be able to use the
+link:http://maven.apache.org/plugins/maven-shade-plugin/[shade plugin]
+to package additional dependencies. Relocating (or renaming) classes
+should not be necessary due to the ClassLoader isolation.
+
+[[ssh]]
+SSH Commands
+------------
+
+Plugins may provide commands that can be accessed through the SSH
+interface (extensions do not have this option).
+
+Command implementations must extend the base class SshCommand:
+
+====
+  import com.google.gerrit.sshd.SshCommand;
+
+  class PrintHello extends SshCommand {
+    protected abstract void run() {
+      stdout.print("Hello\n");
+    }
+  }
+====
+
+If no Guice modules are declared in the manifest, SSH commands may
+use auto-registration by providing an `@Export` annotation:
+
+====
+  import com.google.gerrit.extensions.annotations.Export;
+  import com.google.gerrit.sshd.SshCommand;
+
+  @Export("print")
+  class PrintHello extends SshCommand {
+    protected abstract void run() {
+      stdout.print("Hello\n");
+    }
+  }
+====
+
+If explicit registration is being used, a Guice module must be
+supplied to register the SSH command and declared in the manifest
+with the `Gerrit-SshModule` attribute:
+
+====
+  import com.google.gerrit.sshd.PluginCommandModule;
+
+  class MyCommands extends PluginCommandModule {
+    protected void configureCommands() {
+      command("print").to(PrintHello.class);
+    }
+  }
+====
+
+For a plugin installed as name `helloworld`, the command implemented
+by PrintHello class will be available to users as:
+
+----
+$ ssh -p 29418 review.example.com helloworld print
+----
+
+[[http]]
+HTTP Servlets
+-------------
+
+Plugins or extensions may register additional HTTP servlets, and
+wrap them with HTTP filters.
+
+Servlets may use auto-registration to declare the URL they handle:
+
+====
+  import com.google.gerrit.extensions.annotations.Export;
+  import com.google.inject.Singleton;
+  import javax.servlet.http.HttpServlet;
+  import javax.servlet.http.HttpServletRequest;
+  import javax.servlet.http.HttpServletResponse;
+
+  @Export("/print")
+  @Singleton
+  class HelloServlet extends HttpServlet {
+    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
+      res.setContentType("text/plain");
+      res.setCharacterEncoding("UTF-8");
+      res.getWriter().write("Hello");
+    }
+  }
+====
+
+The auto registration only works for standard servlet mappings like
+`/foo` or `/foo/*`. Regex style bindings must use a Guice ServletModule
+to register the HTTP servlets and declare it explicitly in the manifest
+with the `Gerrit-HttpModule` attribute:
+
+====
+  import com.google.inject.servlet.ServletModule;
+
+  class MyWebUrls extends ServletModule {
+    protected void configureServlets() {
+      serve("/print").with(HelloServlet.class);
+    }
+  }
+====
+
+For a plugin installed as name `helloworld`, the servlet implemented
+by HelloServlet class will be available to users as:
+
+----
+$ curl http://review.example.com/plugins/helloworld/print
+----
+
+[[data-directory]]
+Data Directory
+--------------
+
+Plugins can request a data directory with a `@PluginData` File
+dependency. A data directory will be created automatically by the
+server in `$site_path/data/$plugin_name` and passed to the plugin.
+
+Plugins can use this to store any data they want.
+
+====
+  @Inject
+  MyType(@PluginData java.io.File myDir) {
+    new FileInputStream(new File(myDir, "my.config"));
+  }
+====
+
+[[documentation]]
+Documentation
+-------------
+
+If a plugin does not register a filter or servlet to handle URLs
+`/Documentation/*` or `/static/*`, the core Gerrit server will
+automatically export these resources over HTTP from the plugin JAR.
+
+Static resources under `static/` directory in the JAR will be
+available as `/plugins/helloworld/static/resource`.
+
+Documentation files under `Documentation/` directory in the JAR
+will be available as `/plugins/helloworld/Documentation/resource`.
+
+Documentation may be written in
+link:http://daringfireball.net/projects/markdown/[Markdown] style
+if the file name ends with `.md`. Gerrit will automatically convert
+Markdown to HTML if accessed with extension `.html`.
+
+[[macros]]
+Within the Markdown documentation files macros can be used that allow
+to write documentation with reasonably accurate examples that adjust
+automatically based on the installation.
+
+The following macros are supported:
+
+[width="40%",options="header"]
+|===================================================
+|Macro       | Replacement
+|@PLUGIN@    | name of the plugin
+|@URL@       | Gerrit Web URL
+|@SSH_HOST@  | SSH Host
+|@SSH_PORT@  | SSH Port
+|===================================================
+
+The macros will be replaced when the documentation files are rendered
+from Markdown to HTML.
+
+Macros that start with `\` such as `\@KEEP@` will render as `@KEEP@`
+even if there is an expansion for `KEEP` in the future.
+
+[[auto-index]]
+Automatic Index
+~~~~~~~~~~~~~~~
+
+If a plugin does not handle its `/` URL itself, Gerrit will
+redirect clients to the plugin's `/Documentation/index.html`.
+Requests for `/Documentation/` (bare directory) will also redirect
+to `/Documentation/index.html`.
+
+If neither resource `Documentation/index.html` or
+`Documentation/index.md` exists in the plugin JAR, Gerrit will
+automatically generate an index page for the plugin's documentation
+tree by scanning every `*.md` and `*.html` file in the Documentation/
+directory.
+
+For any discovered Markdown (`*.md`) file, Gerrit will parse the
+header of the file and extract the first level one title. This
+title text will be used as display text for a link to the HTML
+version of the page.
+
+For any discovered HTML (`*.html`) file, Gerrit will use the name
+of the file, minus the `*.html` extension, as the link text. Any
+hyphens in the file name will be replaced with spaces.
+
+If a discovered file name beings with `cmd-` it will be clustered
+into a 'Commands' section of the generated index page. All other
+files are clustered under a 'Documentation' section.
+
+Some optional information from the manifest is extracted and
+displayed as part of the index page, if present in the manifest:
+
+[width="40%",options="header"]
+|===================================================
+|Field       | Source Attribute
+|Name        | Implementation-Title
+|Vendor      | Implementation-Vendor
+|Version     | Implementation-Version
+|URL         | Implementation-URL
+|API Version | Gerrit-ApiVersion
+|===================================================
+
+[[deployment]]
+Deployment
+----------
+
+Compiled plugins and extensions can be deployed to a running Gerrit
+server using the link:cmd-plugin-install.html[plugin install] command.
+
+Plugins can also be copied directly into the server's
+directory at `$site_path/plugins/$name.jar`.  The name of
+the JAR file, minus the `.jar` extension, will be used as the
+plugin name. Unless disabled, servers periodically scan this
+directory for updated plugins. The time can be adjusted by
+link:config-gerrit.html#plugins.checkFrequency[plugins.checkFrequency].
+
+For disabling plugins the link:cmd-plugin-remove.html[plugin remove]
+command can be used.
+
+Disabled plugins can be re-enabled using the
+link:cmd-plugin-enable.html[plugin enable] command.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-release-subproject.txt b/Documentation/dev-release-subproject.txt
new file mode 100644
index 0000000..799ff2d
--- /dev/null
+++ b/Documentation/dev-release-subproject.txt
@@ -0,0 +1,99 @@
+Making a Release of a Gerrit Subproject / Core Plugin
+=====================================================
+
+Preparing a New Snapshot for Publishing
+---------------------------------------
+
+* You will need to have the following in the `pom.xml` to make it
+  deployable to the `gerrit-maven` storage bucket:
+
+----
+  <distributionManagement>
+    <repository>
+      <id>gerrit-maven</id>
+      <name>gerrit Maven Repository</name>
+      <url>s3://gerrit-maven@commondatastorage.googleapis.com</url>
+      <uniqueVersion>true</uniqueVersion>
+    </repository>
+  </distributionManagement>
+----
+
+
+* Add this to the `pom.xml` to enable the wagon provider:
+
+----
+  <build>
+    <extensions>
+      <extension>
+        <groupId>net.anzix.aws</groupId>
+        <artifactId>s3-maven-wagon</artifactId>
+        <version>3.2</version>
+      </extension>
+    </extensions>
+  </build>
+----
+
+
+* Add your username and password to your `~/.m2/settings.xml` file.
+  These need to come from the link:https://code.google.com/apis/console/[API Console].
+
+----
+  <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
+            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+            xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
+    <servers>
+      <server>
+        <id>gerrit-maven</id>
+        <username>GOOG..EXAMPLE.....EXAMPLE</username>
+        <password>EXAMPLE..EXAMPLE..EXAMPLE</password>
+      </server>
+    </servers>
+  </settings>
+----
+
+
+Making a Snapshot
+-----------------
+
+* Only for plugins: in the `pom.xml` update the Gerrit version under
+`properties` > `Gerrit-ApiVersion` to the version of the new Gerrit
+release
+* First build and deploy the latest snapshot and ensure that Gerrit
+builds/runs with this snapshot
+
+* Deploy the snapshot:
+
+====
+  mvn deploy
+====
+
+
+Making a Release
+----------------
+
+* First deploy (and test) the latest snapshot for the subproject/plugin
+
+* Update the top level `pom.xml` in the subproject/plugin to reflect
+the new project version (the exact value of the tag you will create
+below)
+
+* Commit the pom change and push to the project's repo
+`refs/for/<master/stable>`
+
+* Tag the version you just pushed (and push the tag)
+
+====
+ git tag -a -m "prolog-cafe 1.3" v1.3
+ git push gerrit-review refs/tags/v1.3:refs/tags/v1.3
+====
+
+* Deploy the new release:
+
+====
+ mvn deploy
+====
+
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt
new file mode 100644
index 0000000..5ea3042
--- /dev/null
+++ b/Documentation/dev-release.txt
@@ -0,0 +1,279 @@
+Making a Gerrit Release
+=======================
+
+[NOTE]
+========================================================================
+This document is meant primarily for Gerrit maintainers
+who have been given approval and submit status to the Gerrit
+projects.  Additionally, maintainers should be given owner
+status to the Gerrit web site.
+========================================================================
+
+To make a Gerrit release involves a great deal of complex
+tasks and it is easy to miss a step so this document should
+hopefuly serve as both a how to for those new to the process
+and as a checklist for those already familiar with these
+tasks.
+
+
+Gerrit Release Type
+-------------------
+
+Here are some guidelines on release approaches depending on the
+type of release you want to make (stable-fix, stable, RC0, RC1...).
+
+Stable
+~~~~~~
+
+A stable release is generally built from the master branch and may need to
+undergo some stabilization before releasing the final release.
+
+* Propose the release with any plans/objectives to the mailing list
+
+* Create a Gerrit RC0
+
+* If needed create a Gerrit RC1
+
+[NOTE]
+========================================================================
+You may let in a few features to this release
+========================================================================
+
+* If needed create a Gerrit RC2
+
+[NOTE]
+========================================================================
+There should be no new features in this release, only bug fixes
+========================================================================
+
+* Finally create the stable release (no RC)
+
+
+Stable-Fix
+~~~~~~~~~~
+
+Stable-fix releases should likely only contain bug fixes and doc updates.
+
+* Propose the release with any plans/objectives to the mailing list
+
+* This type of release does not need any RCs, release when the objectives
+  are met
+
+
+
+Create the Actual Release
+---------------------------
+
+In the example commands below we assume that the last release was '2.4' and that
+we are preparing '2.5' release.
+
+Prepare the Subprojects
+~~~~~~~~~~~~~~~~~~~~~~~
+
+* Publish the latest snapshot for all subprojects
+* Freeze all subprojects and link:dev-release-subproject.html[publish]
+  them!
+
+
+Prepare Gerrit
+~~~~~~~~~~~~~~
+
+* Create a `stable-2.5` branch for making the new release
+
+* In the `master` branch: Update the poms for the Gerrit version, push for
+review, get merged
+
+====
+ tools/version.sh --snapshot=2.5
+====
+
+* Checkout the `stable-2.5` branch
+* Update the top level `pom.xml` in Gerrit to ensure that none of the
+Subprojects point to snapshot releases
+
+* Tag
+
+====
+ git tag -a -m "gerrit 2.5-rc0" v2.5-rc0
+ git tag -a -m "gerrit 2.5" v2.5
+====
+
+* Build (without plugins)
+
+====
+ ./tools/release.sh
+====
+
+[[plugin-api]]
+Publish the Plugin API JAR File
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* Push JAR to `commondatastorage.googleapis.com`
+** Run `tools/deploy_api.sh`
+
+Prepare the Core Plugins
+~~~~~~~~~~~~~~~~~~~~~~~~
+* link:dev-release-subproject.html[Release and publish] the core plugins
+
+Package Gerrit with Plugins
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* Ensure that the core plugins listed in `gerrit-package-plugins/pom.xml`
+point to the latest release version (no dependency to snapshot versions)
+* Include core plugins into WAR
+====
+ $ ./tools/version.sh --release && mvn clean package -f gerrit-package-plugins/pom.xml
+ $ ./tools/version.sh --reset
+====
+
+* Find WAR that includes the core plugins at
+`gerrit-package-plugins\target\gerrit-full-v2.5.war`
+* Sanity check WAR
+
+Publish to the Project Locations
+--------------------------------
+
+WAR File
+~~~~~~~~
+
+* Upload WAR to code.google.com/p/gerrit (manual web browser)
+** Go to http://code.google.com/p/gerrit/downloads/list
+** Use the "New Download" button
+
+* Update labels:
+** new war: [release-candidate], featured...
+** old war: deprecated
+
+Tag
+~~~
+
+* Push the New Tag
+
+====
+ git push gerrit-review refs/tags/v2.5-rc0:refs/tags/v2.5-rc0
+ git push gerrit-review refs/tags/v2.5:refs/tags/v2.5
+====
+
+
+Docs
+~~~~
+
+====
+ make -C Documentation PRIOR=2.4 update
+ make -C ReleaseNotes update
+====
+
+(no +PRIOR=+... if updating the same release again during RCs)
+
+* Update Google Code project links
+** Go to http://code.google.com/p/gerrit/admin
+** Point the main page to the new docs. The link to the documentation has to be
+updated at two places: in the project description and also in the Links
+section.
+** Point the main page to the new release notes
+
+[NOTE]
+========================================================================
+The docs makefile does an svn cp of the prior revision of the docs to branch
+the docs so you have less to upload on the new docs.
+
+User and password from here:
+
+    https://code.google.com/hosting/settings
+
+If subversion assumes a different username than your google one and asks for a
+password right away simply hit enter. Subversion will fail and then ask for
+another username and password. This time enter the username and password from
+the page linked above. After that subversion should save the username/password
+somewhere under `~/.subversion/auth` folder.
+========================================================================
+
+
+Issues
+~~~~~~
+
+====
+ How do the issues get updated?  Do you run a script to do
+ this?  When do you do it, after the final 2.2.2 is released?
+====
+
+By hand.
+
+Our current process is an issue should be updated to say Status =
+Submitted, FixedIn-2.2.2 once the change is submitted, but before the
+release.
+
+After the release is actually made, you can search in Google Code for
+``Status=Submitted FixedIn=2.2.2'' and then batch update these changes
+to say Status=Released. Make sure the pulldown says ``All Issues''
+because Status=Submitted is considered a closed issue.
+
+
+Mailing List
+~~~~~~~~~~~~
+
+* Send an email to the mailing list to announce the release, consider including some or all of the following in the email:
+** A link to the release and the release notes (if a final release)
+** A link to the docs
+** Describe the type of release (stable, bug fix, RC)
+
+----
+To: Repo and Gerrit Discussion <repo-discuss@googlegroups.com>
+Subject: Announce: Gerrit 2.2.2.1  (Stable bug fix update)
+
+I am pleased to announce Gerrit Code Review 2.2.2.1.
+
+Download:
+
+  http://code.google.com/p/gerrit/downloads/list
+
+
+This release is a stable bug fix release with some
+documentation updates including a new "Contributing to
+Gerrit" doc:
+
+  http://gerrit-documentation.googlecode.com/svn/Documentation/2.2.2/dev-contributing.html
+
+
+To read more about the bug fixes:
+
+  http://gerrit-documentation.googlecode.com/svn/ReleaseNotes/ReleaseNotes-2.2.2.1.html
+
+-Martin
+----
+
+* Add an entry to the NEWS section of the main Gerrit project web page
+** Go to: http://code.google.com/p/gerrit/admin
+** Add entry like:
+----
+ * Jun 14, 2012 - Gerrit 2.4.1 [https://groups.google.com/d/topic/repo-discuss/jHg43gixqzs/discussion Released]
+----
+
+* Update the new discussion group announcement to be sticky
+** Go to: http://groups.google.com/group/repo-discuss/topics
+** Click on the announcement thread
+** Near the top right, click on options
+** Under options, cick the "Display this top first" checkbox
+** and Save
+
+* Update the previous discussion group announcement to no longer be sticky
+** See above (unclick checkbox)
+
+
+Merging Stable Fixes to master
+------------------------------
+
+After every stable-fix release, stable should be merged to master to
+ensure that none of the fixes ever get lost.
+
+====
+ git config merge.summary true
+ git checkout master
+ git reset --hard origin/master
+ git branch -f stable origin/stable
+ git merge stable
+====
+
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/error-branch-not-found.txt b/Documentation/error-branch-not-found.txt
index e2dcff1..2aad0e1 100644
--- a/Documentation/error-branch-not-found.txt
+++ b/Documentation/error-branch-not-found.txt
@@ -7,8 +7,8 @@
 To push a change for code review the commit has to be pushed to the
 project's magical `refs/for/'branch'` ref (for details have a look at
 link:user-upload.html#push_create[Create Changes]).
-If you specify a non existing branch in the `refs/for/'branch'` ref
-the push is failing with the error message 'branch ... not found'.
+If you specify a non-existing branch in the `refs/for/'branch'` ref
+the push fails with the error message 'branch ... not found'.
 
 To fix this problem verify
 
diff --git a/Documentation/error-change-closed.txt b/Documentation/error-change-closed.txt
index 7170a65..3244fb3 100644
--- a/Documentation/error-change-closed.txt
+++ b/Documentation/error-change-closed.txt
@@ -1,8 +1,11 @@
 change ... closed
 =================
 
-With this error message Gerrit rejects to push a commit to a change
-that is already closed.
+With this error message Gerrit rejects to push a commit or submit a
+review label (approval) to a change that is already closed.
+
+When Pushing a Commit
+---------------------
 
 This error occurs if you are trying to push a commit that contains
 the Change-Id of a closed change in its commit message. A change can
@@ -14,7 +17,7 @@
 new change. To do this you have to remove the Change-Id from the
 commit message as explained link:error-push-fails-due-to-commit-message.html[here] and ideally generate a new Change-Id
 using the link:cmd-hook-commit-msg.html[commit hook] or EGit. Before pushing again it is also
-recommendable to do a link:http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html[git rebase] to base your commit on the submitted
+recommended to do a link:http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html[git rebase] to base your commit on the submitted
 change. Pushing again should now create a new change in Gerrit.
 
 If the change for which you wanted to upload a new patch set was
@@ -24,6 +27,14 @@
 'Restore Change' button). Afterwards the push should succeed and a
 new patch set for this change will be created.
 
+When Submitting a Review Label
+------------------------------
+
+This error occurs if you are trying to submit a review label (approval) using
+the link:cmd-review.html[ssh review command] after the change has been closed.
+A change can be closed because it was submitted and merged, because it was abandoned,
+or because the patchset to which you are submitting the review has been replaced
+by a newer patchset.
 
 GERRIT
 ------
diff --git a/Documentation/error-change-does-not-belong-to-project.txt b/Documentation/error-change-does-not-belong-to-project.txt
index 29957e1..e747881 100644
--- a/Documentation/error-change-does-not-belong-to-project.txt
+++ b/Documentation/error-change-does-not-belong-to-project.txt
@@ -7,7 +7,7 @@
 This error message means that the user explicitly pushed a commit to
 a change that belongs to another project by specifying it as target
 ref. This way of adding a new patch set to a change is deprecated as
-explained link:user-upload.html#manual_replacement_mapping[here]. It is recommended to only rely on Change-ID's for
+explained link:user-upload.html#manual_replacement_mapping[here]. It is recommended to only rely on Change-IDs for
 link:user-upload.html#push_replace[replacing changes].
 
 
diff --git a/Documentation/error-change-not-found.txt b/Documentation/error-change-not-found.txt
index c9ac0d8..b6df13b 100644
--- a/Documentation/error-change-not-found.txt
+++ b/Documentation/error-change-not-found.txt
@@ -7,7 +7,7 @@
 This error message means that the user explicitly pushed a commit to
 a non-existing change by specifying it as target ref. This way of
 adding a new patch set to a change is deprecated as explained link:user-upload.html#manual_replacement_mapping[here].
-It is recommended to only rely on Change-ID's for link:user-upload.html#push_replace[replacing changes].
+It is recommended to only rely on Change-IDs for link:user-upload.html#push_replace[replacing changes].
 
 
 GERRIT
diff --git a/Documentation/error-you-are-not-author.txt b/Documentation/error-invalid-author.txt
similarity index 88%
rename from Documentation/error-you-are-not-author.txt
rename to Documentation/error-invalid-author.txt
index a245252..c484776 100644
--- a/Documentation/error-you-are-not-author.txt
+++ b/Documentation/error-invalid-author.txt
@@ -1,10 +1,10 @@
-you are not author ...
-======================
+invalid author
+==============
 
-Gerrit verifies for every pushed commit that the e-mail address of
+For every pushed commit Gerrit verifies that the e-mail address of
 the author matches one of the registered e-mail addresses of the
 pushing user. If this is not the case pushing the commit fails with
-the error message "you are not author ...". This policy can be
+the error message "invalid author". This policy can be
 bypassed by having the access right
 link:access-control.html#category_forge_author['Forge Author'].
 
@@ -17,8 +17,8 @@
 Incorrect configuration of the e-mail address on client or server side
 ----------------------------------------------------------------------
 
-If pushing to Gerrit fails with the error message "you are not
-author ..." and you are the author of the commit for which the push
+If pushing to Gerrit fails with the error message "invalid author"
+and you are the author of the commit for which the push
 fails, then either you have not successfully registered this e-mail
 address for your Gerrit account or the author information of the
 pushed commit is incorrect.
@@ -27,7 +27,7 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Check in Gerrit under 'Settings -> Identities' which e-mail addresses
-you've configured for your Gerrit account, if no e-mail address is
+you've configured for your Gerrit account.  If no e-mail address is
 registered go to 'Settings -> Contact Information' and register a new
 e-mail address there. Make sure you confirm your e-mail address by
 clicking on the link in the e-mail verification mail sent by Gerrit.
@@ -92,7 +92,7 @@
 git rebase for the affected commits. While doing the interactive
 rebase you have to choose 'edit' for those commits for which the
 author should be rewritten. When the rebase stops at such a commit
-you have to amend the commit with explicitly setting the author
+you have to amend the commit, explicitly setting the author
 before continuing the rebase.
 
 Here is an example that shows how the interactive rebase is used to
@@ -131,8 +131,8 @@
 Missing privileges to push commits of other users
 -------------------------------------------------
 
-If pushing to Gerrit fails with the error message "you are not
-author ..." and somebody else is author of the commit for which the
+If pushing to Gerrit fails with the error message "invalid author"
+and somebody else is author of the commit for which the
 push fails, then you have no permissions to forge the author
 identity. In this case you may contact the project owner to request
 the access right '+1 Forge Author Identity' in the 'Forge Identity'
diff --git a/Documentation/error-you-are-not-committer.txt b/Documentation/error-invalid-committer.txt
similarity index 87%
rename from Documentation/error-you-are-not-committer.txt
rename to Documentation/error-invalid-committer.txt
index b5b8c44..447064e 100644
--- a/Documentation/error-you-are-not-committer.txt
+++ b/Documentation/error-invalid-committer.txt
@@ -1,10 +1,10 @@
-you are not committer ...
-=========================
+invalid committer
+=================
 
-Gerrit verifies for every pushed commit that the e-mail address of
+For every pushed commit Gerrit verifies that the e-mail address of
 the committer matches one of the registered e-mail addresses of the
 pushing user. If this is not the case pushing the commit fails with
-the error message "you are not committer ...". This policy can be
+the error message "invalid committer". This policy can be
 bypassed by having the access right
 link:access-control.html#category_forge_committer['Forge Committer'].
 
@@ -19,8 +19,8 @@
 Incorrect configuration of the e-mail address on client or server side
 ----------------------------------------------------------------------
 
-If pushing to Gerrit fails with the error message "you are not
-committer ..." and you committed the change for which the push fails,
+If pushing to Gerrit fails with the error message "invalid committer"
+and you committed the change for which the push fails,
 then either you have not successfully registered this e-mail address
 for your Gerrit account or the committer information of the pushed
 commit is incorrect.
@@ -29,7 +29,7 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Check in Gerrit under 'Settings -> Identities' which e-mail addresses
-you've configured for your Gerrit account, if no e-mail address is
+you've configured for your Gerrit account.  If no e-mail address is
 registered go to 'Settings -> Contact Information' and register a new
 e-mail address there. Make sure you confirm your e-mail address by
 clicking on the link in the e-mail verification mail sent by Gerrit.
@@ -96,8 +96,8 @@
 Missing privileges to push commits that were committed by other users
 ---------------------------------------------------------------------
 
-If pushing to Gerrit fails with the error message "you are not
-committer ..." and somebody else committed the change for which the
+If pushing to Gerrit fails with the error message "invalid committer"
+and somebody else committed the change for which the
 push fails, then you have no permissions to forge the committer
 identity. In this case you may contact the project owner to request
 the access right '+2 Forge Committer or Tagger Identity' in the
diff --git a/Documentation/error-messages.txt b/Documentation/error-messages.txt
index f915a14..c9df883 100644
--- a/Documentation/error-messages.txt
+++ b/Documentation/error-messages.txt
@@ -15,7 +15,9 @@
 * link:error-change-not-found.html[change ... not found]
 * link:error-contains-banned-commit.html[contains banned commit ...]
 * link:error-has-duplicates.html[... has duplicates]
+* link:error-invalid-author.html[invalid author]
 * link:error-invalid-changeid-line.html[invalid Change-Id line format in commit message]
+* link:error-invalid-committer.html[invalid committer]
 * link:error-missing-changeid.html[missing Change-Id in commit message]
 * link:error-multiple-changeid-lines.html[multiple Change-Id lines in commit message]
 * link:error-no-changes-made.html[no changes made]
@@ -33,8 +35,6 @@
 * link:error-squash-commits-first.html[squash commits first]
 * link:error-upload-denied.html[Upload denied for project \'...']
 * link:error-not-allowed-to-upload-merges.html[you are not allowed to upload merges]
-* link:error-you-are-not-author.html[you are not author ...]
-* link:error-you-are-not-committer.html[you are not committer ...]
 
 
 General Hints
diff --git a/Documentation/error-no-changes-made.txt b/Documentation/error-no-changes-made.txt
index 7ef7082..d0e1d4f 100644
--- a/Documentation/error-no-changes-made.txt
+++ b/Documentation/error-no-changes-made.txt
@@ -2,10 +2,10 @@
 ===============
 
 With this error message Gerrit rejects to push a commit as a new
-patch set for a change, if the pushed commit is identical with the
+patch set for a change, if the pushed commit is identical to the
 current patch set of this change.
 
-A pushed commit is considered to be identical with the current patch
+A pushed commit is considered to be identical to the current patch
 set if
 
 - the files in the commit,
diff --git a/Documentation/error-no-new-changes.txt b/Documentation/error-no-new-changes.txt
index 347c080..8e409ef 100644
--- a/Documentation/error-no-new-changes.txt
+++ b/Documentation/error-no-new-changes.txt
@@ -3,7 +3,7 @@
 
 With this error message Gerrit rejects to push a commit if the pushed
 commit was already successfully pushed to Gerrit. In this case there
-is no new change and consequently there is nothing to do for Gerrit.
+is no new change and consequently there is nothing for Gerrit to do.
 
 If your push is failing with this error message, you normally
 don't have to do anything since the commit was already successfully
diff --git a/Documentation/error-non-fast-forward.txt b/Documentation/error-non-fast-forward.txt
index 7dba51b..6604e10 100644
--- a/Documentation/error-non-fast-forward.txt
+++ b/Documentation/error-non-fast-forward.txt
@@ -1,15 +1,15 @@
 non-fast forward
 ================
 
-With this error message Git rejects a push if the remote branch can't
+With this error message Gerrit rejects a push if the remote branch can't
 be fast forwarded onto the pushed commit. This is the case if the
 pushed commit is not based on the current tip of the remote branch.
 
 If a non-fast forward update would be done, all commits from the
 remote branch that succeed the base commit of the pushed commit would
 be removed. This would be especially confusing for other users that
-have based their work on such a commit. Because of this Git is by
-default not allowing non-fast forward updates.
+have based their work on such a commit. Because of this Git by
+default does not allow non-fast forward updates.
 
 When working with Gerrit, this error can only occur if
 link:user-upload.html#bypass_review[code review is bypassed].
@@ -46,7 +46,7 @@
 the commit to the correct project.
 
 
-Although it is considered as bad practice, it is possible to allow
+Although it is considered bad practice, it is possible to allow
 non-fast forward updates with Git. For this the remote Git repository
 has to be configured to not deny non-fast forward updates (set the
 link:http://www.kernel.org/pub/software/scm/git/docs/git-config.html[Git configuration] parameter 'receive.denyNonFastForwards' to
diff --git a/Documentation/error-not-a-gerrit-administrator.txt b/Documentation/error-not-a-gerrit-administrator.txt
index 0468d83..b771af6 100644
--- a/Documentation/error-not-a-gerrit-administrator.txt
+++ b/Documentation/error-not-a-gerrit-administrator.txt
@@ -1,7 +1,7 @@
 Not a Gerrit administrator
 ==========================
 
-With this error message Gerrit rejects to execute a SSH command that
+With this error message Gerrit rejects to execute an SSH command that
 requires administrator privileges if the user is not a Gerrit
 administrator.
 
diff --git a/Documentation/error-not-a-gerrit-project.txt b/Documentation/error-not-a-gerrit-project.txt
index 368a102..dac98ae 100644
--- a/Documentation/error-not-a-gerrit-project.txt
+++ b/Documentation/error-not-a-gerrit-project.txt
@@ -18,7 +18,7 @@
   project is listed. If the project is not listed the project either
   does not exist or you don't have
   link:access-control.html#category_read['Read'] access for it. This
-  means if you certain that the project name is right you should
+  means if you are certain that the project name is right you should
   contact the Gerrit Administrator or project owner to request access
   to the project.
 
diff --git a/Documentation/error-not-allowed-to-upload-merges.txt b/Documentation/error-not-allowed-to-upload-merges.txt
index 981ba91..515eef5 100644
--- a/Documentation/error-not-allowed-to-upload-merges.txt
+++ b/Documentation/error-not-allowed-to-upload-merges.txt
@@ -2,11 +2,11 @@
 ====================================
 
 With this error message Gerrit rejects to push a merge commit if the
-pushing user has no permissions to upload merge commits for the
+pushing user has no permission to upload merge commits for the
 project to which the push is done.
 
 If you need to upload merge commits, you can contact one of the
-project owners and request permissions to upload merge commits
+project owners and request permission to upload merge commits
 (access right link:access-control.html#category_push_merge['Push Merge Commit'])
 for this project.
 
diff --git a/Documentation/error-permission-denied.txt b/Documentation/error-permission-denied.txt
index 1cb5708..2ec0a3f 100644
--- a/Documentation/error-permission-denied.txt
+++ b/Documentation/error-permission-denied.txt
@@ -1,7 +1,7 @@
 Permission denied (publickey)
 =============================
 
-With this error message a SSH command to Gerrit is rejected if the
+With this error message an SSH command to Gerrit is rejected if the
 SSH authentication is not successful.
 
 The link:http://en.wikipedia.org/wiki/Secure_Shell[SSH] protocol uses link:http://en.wikipedia.org/wiki/Public-key_cryptography[Public-key Cryptography] for authentication.
diff --git a/Documentation/error-prohibited-by-gerrit.txt b/Documentation/error-prohibited-by-gerrit.txt
index 69f80c1..bad2b3c 100644
--- a/Documentation/error-prohibited-by-gerrit.txt
+++ b/Documentation/error-prohibited-by-gerrit.txt
@@ -17,10 +17,17 @@
 3. if you push an annotated tag without
    link:access-control.html#category_push_annotated['Push Annotated Tag']
    access right on 'refs/tags/*'
-4. if you push a lightweight tag without the access right link:access-control.html#category_create['Create
+4. if you push a signed tag without
+   link:access-control.html#category_push_signed['Push Signed Tag']
+   access right on 'refs/tags/*'
+5. if you push a lightweight tag without the access right link:access-control.html#category_create['Create
    Reference'] for the reference name 'refs/tags/*'
+6. if you push a tag with somebody else as tagger and you don't have the
+   link:access-control.html#category_forge_committer['Forge Committer']
+   access right for the reference name 'refs/tags/*'
+7. if you push to a project that is in state 'Read Only'
 
-For new users it happens often that they accidentally try to bypass
+For new users it often happens that they accidentally try to bypass
 code review. The push then fails with the error message 'prohibited
 by Gerrit' because the project didn't allow to bypass code review.
 Bypassing the code review is done by pushing directly to refs/heads/*
diff --git a/Documentation/error-push-fails-due-to-commit-message.txt b/Documentation/error-push-fails-due-to-commit-message.txt
index 01e0a8e..172d64f 100644
--- a/Documentation/error-push-fails-due-to-commit-message.txt
+++ b/Documentation/error-push-fails-due-to-commit-message.txt
@@ -3,7 +3,7 @@
 
 If Gerrit rejects pushing a commit it is often the case that there is
 an issue with the commit message of the pushed commit. In this case
-often the problem can be resolved by fixing the commit message.
+the problem can often be resolved by fixing the commit message.
 
 If the commit message of the last commit needs to be fixed you can
 simply amend the last commit (please find a detailed description in
diff --git a/Documentation/error-squash-commits-first.txt b/Documentation/error-squash-commits-first.txt
index 138ad98..2181c52 100644
--- a/Documentation/error-squash-commits-first.txt
+++ b/Documentation/error-squash-commits-first.txt
@@ -9,7 +9,7 @@
 prevents such dependencies between patch sets within the same change
 to keep the review process simple. Otherwise reviewers would not only
 have to review the latest patch set but also all the patch sets the
-latest one is depending on.
+latest one depends on.
 
 This error is quite common, it appears when a user tries to address
 review comments and creates a new commit instead of amending the
@@ -93,8 +93,8 @@
 ----
 
 If it was the intention to create a patch series with multiple
-changes to be reviewed each commit message should contain the
-Change-ID of the corresponding change in Gerrit, if a change in
+changes to be reviewed, each commit message should contain the
+Change-ID of the corresponding change in Gerrit.  If a change in
 Gerrit does not exist yet, the Change-ID should be generated (either
 by using a link:cmd-hook-commit-msg.html[commit hook] or by using EGit) or the Change-ID could be
 removed (not recommended since then amending this commit to create
diff --git a/Documentation/i18n-readme.txt b/Documentation/i18n-readme.txt
index 080ecb6..a84c3dc 100644
--- a/Documentation/i18n-readme.txt
+++ b/Documentation/i18n-readme.txt
@@ -1,7 +1,7 @@
 Gerrit Code Review - i18n
 =========================
 
-Aside from actually writing translations, there's some issues with
+Aside from actually writing translations, there are some issues with
 the way the code produces output.  Most of the UI should support
 right-to-left (RTL) languages.
 
diff --git a/Documentation/index.txt b/Documentation/index.txt
index 5143bf7..4c2335f 100644
--- a/Documentation/index.txt
+++ b/Documentation/index.txt
@@ -18,7 +18,13 @@
 * link:user-signedoffby.html[Signed-off-by Lines]
 * link:access-control.html[Access Controls]
 * link:error-messages.html[Error Messages]
+* link:rest-api.html[REST API]
+* link:user-custom-dashboards.html[Custom Dashboards]
+* link:user-notify.html[Subscribing to Email Notifications]
 * link:user-submodules.html[Subscribing to Git Submodules]
+* link:refs-notes-review.html[The `refs/notes/review` namespace]
+* link:prolog-cookbook.html[Prolog Cookbook]
+* link:prolog-change-facts.html[Prolog Facts for Gerrit Changes]
 
 Installation
 ------------
@@ -33,7 +39,6 @@
 
 * link:config-gerrit.html[System Settings]
 * link:config-contact.html[User Contact Information]
-* link:config-replication.html[Git Replication/Mirroring]
 * link:config-gitweb.html[Gitweb Integration]
 * link:config-headerfooter.html[Site Header/Footer]
 * link:config-sso.html[Single Sign-On Systems]
@@ -47,8 +52,11 @@
 * link:dev-readme.html[Developer Setup]
 * link:dev-eclipse.html[Eclipse Setup]
 * link:dev-contributing.html[Contributing to Gerrit]
+* link:dev-plugins.html[Developing Plugins]
 * link:dev-design.html[System Design]
 * link:i18n-readme.html[i18n Support]
+* link:dev-release.html[Developer Release]
+* link:dev-release-subproject.html[Developer Subproject Release]
 
 Resources
 ---------
@@ -56,5 +64,5 @@
 * link:http://code.google.com/p/gerrit/[Homepage]
 * link:http://code.google.com/p/gerrit/downloads/list[Downloads]
 * link:http://code.google.com/p/gerrit/issues/list[Issue Tracking]
-* link:http://code.google.com/p/gerrit/wiki/Source?tm=4[Source Code]
+* 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]
diff --git a/Documentation/install-j2ee.txt b/Documentation/install-j2ee.txt
index 507d6c5..96814a0 100644
--- a/Documentation/install-j2ee.txt
+++ b/Documentation/install-j2ee.txt
@@ -44,7 +44,7 @@
 +
 If you enabled Bouncy Castle Crypto during 'init', copy the JAR
 from `'$site_path'/lib` into your servlet container's extensions
-directory so its available to Gerrit Code Review.
+directory so it's available to Gerrit Code Review.
 
 
 Jetty 7.x
diff --git a/Documentation/install-quick.txt b/Documentation/install-quick.txt
index 6bea7f8..c09c197 100644
--- a/Documentation/install-quick.txt
+++ b/Documentation/install-quick.txt
@@ -12,7 +12,7 @@
 flavors or BSD.
 
 It's also presumed that you have access to an OpenID enabled email address.
-Examples of OpenID enable email providers are gmail, yahoo and hotmail.
+Examples of OpenID enable email providers are Gmail, Yahoo! Mail and Hotmail.
 It's also possible to register a custom email address with OpenID, but that is
 outside the scope of this quick installation guide. For testing purposes one of
 the above providers should be fine. Please note that network access to the
@@ -42,7 +42,7 @@
 Create a user to host the Gerrit service
 ----------------------------------------
 
-We will run the service as a non privileged user on your system.
+We will run the service as a non-privileged user on your system.
 First create the user and then become the user:
 
 ----
@@ -50,7 +50,7 @@
   $ sudo su gerrit2
 ----
 
-If you don't have root privileges you could skip this step and run gerrit
+If you don't have root privileges you could skip this step and run Gerrit
 as your own user as well.
 
 
@@ -58,7 +58,7 @@
 Download Gerrit
 ---------------
 
-It's time to download the archive that contains the gerrit web and ssh service.
+It's time to download the archive that contains the Gerrit web and ssh service.
 
 You can choose from different versions to download from here:
 
@@ -87,14 +87,28 @@
 When the init is complete, you can review your settings in the
 file `'$site_path/etc/gerrit.config'`.
 
-An important setting will be the canonicalWebUrl which will
-be needed later to access gerrit's web interface.
+Note that initialization also starts the server.  If any settings changes are
+made, the server must be restarted before they will take effect.
 
 ----
-  gerrit2@host:~$ cat ~/gerrit_testsite/etc/gerrit.config | grep canonical
-  canonicalWebUrl = http://localhost:8080/
+  gerrit2@host:~$ ~/gerrit_testsite/bin/gerrit.sh restart
+  Stopping Gerrit Code Review: OK
+  Starting Gerrit Code Review: OK
   gerrit2@host:~$
 ----
+
+The server can be also stopped and started by passing the `stop` and `start`
+commands to gerrit.sh.
+
+----
+  gerrit2@host:~$ ~/gerrit_testsite/bin/gerrit.sh stop
+  Stopping Gerrit Code Review: OK
+  gerrit2@host:~$
+  gerrit2@host:~$ ~/gerrit_testsite/bin/gerrit.sh start
+  Starting Gerrit Code Review: OK
+  gerrit2@host:~$
+----
+
 [[usersetup]]
 The first user
 --------------
@@ -154,15 +168,32 @@
 Registering your key in Gerrit
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Open a browser and enter the canonical url you got before when
-initializing Gerrit.
+Open a browser and enter the canonical url of your Gerrit server.  You can
+find the url in the settings file.
 
 ----
-  Canonical URL                [http://localhost:8080/]:
+  gerrit2@host:~$ git config -f ~/gerrit_testsite/etc/gerrit.config gerrit.canonicalWebUrl
+  http://localhost:8080/
+  gerrit2@host:~$
 ----
 
 Register a new account in Gerrit through the web interface with the
 email address of your choice.
+
+The default authentication type is OpenID.  If your Gerrit server is behind a
+proxy, and you are using an external OpenID provider, you will need to add the
+proxy settings in the configuration file.
+
+----
+  gerrit2@host:~$ git config -f ~/gerrit_testsite/etc/gerrit.config --add http.proxy http://proxy:8080
+  gerrit2@host:~$ git config -f ~/gerrit_testsite/etc/gerrit.config --add http.proxyUsername username
+  gerrit2@host:~$ git config -f ~/gerrit_testsite/etc/gerrit.config --add http.proxyPassword password
+----
+
+Refer to the Gerrit configuration guide for more detailed information about
+link:config-gerrit.html#auth[authentication] and
+link:config-gerrit.html#http.proxy[proxy] settings.
+
 The first user to sign-in and register an account will be
 automatically placed into the fully privileged Administrators group,
 permitting server management over the web and over SSH.  Subsequent
@@ -216,7 +247,7 @@
 Your base Gerrit server is now running and you have a user that's ready
 to interact with it.  You now have two options, either you create a new
 test project to work with or you already have a git with history that
-you would like to import into gerrit and try out code review on.
+you would like to import into Gerrit and try out code review on.
 
 New project from scratch
 ~~~~~~~~~~~~~~~~~~~~~~~~
@@ -231,14 +262,14 @@
   user@host:~$
 ----
 
-This will create a repository that you could clone to work with.
+This will create a repository that you can clone to work with.
 
 Already existing project
 ~~~~~~~~~~~~~~~~~~~~~~~~
 
 The other alternative is if you already have a git project that you
 want to try out Gerrit on.
-First you have to create the project, this is done via the SSH port:
+First you have to create the project.  This is done via the SSH port:
 
 ----
   user@host:~$ ssh -p 29418 user@localhost gerrit create-project --name demo-project
@@ -262,7 +293,7 @@
   user@host:~/my-project$
 ----
 
-This will create a repository that you could clone to work with.
+This will create a repository that you can clone to work with.
 
 
 My first change
@@ -294,7 +325,7 @@
 
 Usually when you push to a remote git, you push to the reference
 `'/refs/heads/branch'`, but when working with Gerrit you have to push to a
-virtual branch representing "code review before submittal to branch".
+virtual branch representing "code review before submission to branch".
 This virtual name space is known as /refs/for/<branch>
 
 ----
@@ -319,11 +350,11 @@
 ---------------------------
 
 This covers the scope of getting Gerrit started and your first change uploaded.
-It doesn't give any clue as to how the review workflow works, please find
+It doesn't give any clue as to how the review workflow works, please read
 link:http://source.android.com/submit-patches/workflow[Default Workflow] to
 learn more about the workflow of Gerrit.
 
-To read more on the installation of Gerrit please read link:install.html[the detailed
+To read more on the installation of Gerrit please see link:install.html[the detailed
 installation page].
 
 
diff --git a/Documentation/install.txt b/Documentation/install.txt
index b90bbce..9926b8f 100644
--- a/Documentation/install.txt
+++ b/Documentation/install.txt
@@ -3,7 +3,7 @@
 
 [[requirements]]
 Requirements
------------
+------------
 To run the Gerrit service, the following requirements must be met on
 the host:
 
@@ -199,11 +199,10 @@
 ------------------
 
 Gerrit Code Review supports some site-specific customization options.
-For more information, see the related topic in this manual:
+For more information, see the related topics in this manual:
 
 * link:config-reverseproxy.html[Reverse Proxy]
 * link:config-sso.html[Single Sign-On Systems]
-* link:config-replication.html[Git Replication/Mirroring]
 * link:config-headerfooter.html[Site Header/Footer]
 * link:config-gitweb.html[Gitweb Integration]
 * link:config-gerrit.html[Other System Settings]
@@ -222,6 +221,13 @@
 * http://www.kernel.org/pub/software/scm/git/docs/git-daemon.html[man git-daemon]
 
 
+[[plugins]]
+Plugins
+-------
+
+Place Gerrit plugins in the review_site/plugins directory to have them loaded on Gerrit startup.
+
+
 External Documentation Links
 ----------------------------
 
diff --git a/Documentation/intro-quick.txt b/Documentation/intro-quick.txt
index 3d5cbcb..25f5d5e 100644
--- a/Documentation/intro-quick.txt
+++ b/Documentation/intro-quick.txt
@@ -27,7 +27,7 @@
 simple for all committers on a project to ensure that changes are
 checked over before they're actually applied. Because of this Gerrit
 is equally useful where all users are trusted committers such as may
-the case with closed-source commercial development. Either way it's
+be the case with closed-source commercial development. Either way it's
 still desirable to have code reviewed to improve the quality and
 maintainability of the code. After all, if only one person has seen
 the code it may be a little difficult to maintain when that person
@@ -337,7 +337,7 @@
 Easy as that, we now have the change in our working copy to play with.
 You might be interested in what the numbers of the refspec mean.
 
-* The first *68* is the id if the change +mod 100+.  The only reason
+* The first *68* is the id of the change +mod 100+.  The only reason
 for this initial number is to reduce the number of files in any given
 directory within the git repository.
 * The second *68* is the full id of the change. You'll notice this in
@@ -379,7 +379,7 @@
 that can be done by different users, Submission is a third operation
 that can be limited down to another group of users.
 
-Activating the _Publish and Submit_ or _Submit Patch Set X_ button
+Clicking the _Publish and Submit_ or _Submit Patch Set X_ button
 will merge the change into the main part of the repository so that it
 becomes an accepted part of the project. After this anyone fetching
 the git repository will receive this change as a part of the master
diff --git a/Documentation/json.txt b/Documentation/json.txt
index b1dbc32..f0588ce 100644
--- a/Documentation/json.txt
+++ b/Documentation/json.txt
@@ -11,22 +11,22 @@
 ------
 The Gerrit change being reviewed, or that was already reviewed.
 
-project:: Project path in Gerrit
+project:: Project path in Gerrit.
 
-branch:: Branch name within project
+branch:: Branch name within project.
 
-topic:: Topic name specified by the uploader for this change series
+topic:: Topic name specified by the uploader for this change series.
 
 id:: Change identifier, as scraped out of the Change-Id field in
 the commit message, or as assigned by the server if it was missing.
 
-number:: Change number (deprecated)
+number:: Change number (deprecated).
 
-subject:: Description of change
+subject:: Description of change.
 
-owner:: Owner in <<account,account attribute>>
+owner:: Owner in <<account,account attribute>>.
 
-url:: Canonical URL to reach this change
+url:: Canonical URL to reach this change.
 
 commitMessage:: The full commit message for the change.
 
@@ -53,9 +53,12 @@
 message based on the server's
 link:config-gerrit.html#trackingid[trackingid] sections.
 
-currentPatchSet:: Current <<patchset,patchset attribute>>.
+currentPatchSet:: Current <<patchSet,patchSet attribute>>.
 
-patchSets:: All <<patchset,patchset attribute>> for this change.
+patchSets:: All <<patchSet,patchSet attribute>> for this change.
+
+submitRecords:: The <<submitRecord,submitRecord attribute>> contains
+information about whether this change has been or can be submitted.
 
 [[trackingid]]
 trackingid
@@ -76,8 +79,10 @@
 
 email:: User's preferred email address.
 
-[[patchset]]
-patchset
+username:: User's username, if configured.
+
+[[patchSet]]
+patchSet
 --------
 Refers to a specific patchset within a <<change,change>>.
 
@@ -109,8 +114,8 @@
 
 by:: Reviewer of the patch set in <<account,account attribute>>.
 
-[[refupdate]]
-refupdate
+[[refUpdate]]
+refUpdate
 --------
 Information about a ref that was updated.
 
@@ -118,10 +123,61 @@
 
 newRev:: The new value the ref was updated to.
 
-project:: Project path in Gerrit
+project:: Project path in Gerrit.
 
 refName:: Ref name within project.
 
+[[queryLimit]]
+queryLimit
+----------
+Information about the link:access-control.html#capability_queryLimit[queryLimit]
+of a user.
+
+min:: lower limit
+
+max:: upper limit
+
+[[submitRecord]]
+submitRecord
+------------
+Information about the submit status of a change.
+
+status:: Current submit status.
+
+  OK;; The change is ready for submission or already submitted.
+
+  NOT_READY;; The change is missing a required label.
+
+  RULE_ERROR;; An internal server error occurred preventing computation.
+
+labels:: This describes the state of each code review
+<<label,label attribute>>, unless the status is RULE_ERROR.
+
+[[label]]
+label
+-----
+Information about a code review label for a change.
+
+label:: The name of the label.
+
+status:: The status of the label.
+
+  OK;; This label provides what is necessary for submission.
+
+  REJECT;; This label prevents the change from being submitted.
+
+  NEED;; The label is required for submission, but has not
+  been satisfied.
+
+  MAY;; The label may be set, but it's neither necessary for
+  submission nor does it block submission if set.
+
+  IMPOSSIBLE;; The label is required for submission, but is impossible
+  to complete.  The likely cause is access has not been granted
+  correctly by the project owner or site administrator.
+
+by:: The <<account,account>> that applied the label.
+
 SEE ALSO
 --------
 
diff --git a/Documentation/licenses.txt b/Documentation/licenses.txt
index 69018d8..7787fe8 100644
--- a/Documentation/licenses.txt
+++ b/Documentation/licenses.txt
@@ -18,6 +18,7 @@
 |Google Gson                | <<apache2,Apache License 2.0>>
 |Google Web Toolkit         | <<apache2,Apache License 2.0>>
 |Guice                      | <<apache2,Apache License 2.0>>
+|Guava Libraries            | <<apache2,Apache License 2.0>>
 |Apache Commons Codec       | <<apache2,Apache License 2.0>>
 |Apache Commons DBCP        | <<apache2,Apache License 2.0>>
 |Apache Commons Http Client | <<apache2,Apache License 2.0>>
@@ -27,13 +28,12 @@
 |Apache Commons Pool        | <<apache2,Apache License 2.0>>
 |Apache Log4J               | <<apache2,Apache License 2.0>>
 |Apache MINA                | <<apache2,Apache License 2.0>>
-|Apache Tomact Servlet API  | <<apache2,Apache License 2.0>>
+|Apache Tomcat Servlet API  | <<apache2,Apache License 2.0>>
 |Apache SSHD                | <<apache2,Apache License 2.0>>, see also <<sshd,NOTICE>>
 |Apache Velocity            | <<apache2,Apache License 2.0>>
 |Apache Xerces              | <<apache2,Apache License 2.0>>
 |OpenID4Java                | <<apache2,Apache License 2.0>>
 |Neko HTML                  | <<apache2,Apache License 2.0>>
-|Ehcache                    | <<apache2,Apache License 2.0>>
 |mime-util                  | <<apache2,Apache License 2.0>>
 |Jetty                      | <<apache2,Apache License 2.0>>, or link:http://www.eclipse.org/legal/epl-v10.html[EPL]
 |Prolog Cafe                | <<prolog_cafe,EPL or GPL>>
@@ -52,6 +52,7 @@
 |JSR 305                    | <<jsr305,New-Style BSD>>
 |dk.brics.automaton         | <<automaton,New-Style BSD>>
 |Java Concurrency in Practice Annotations | <<jcip,Create Commons Attribution License>>
+|pegdown                    | <<apache2,Apache License 2.0>>
 |======================================================================
 
 Cryptography Notice
diff --git a/Documentation/pgm-ExportReviewNotes.txt b/Documentation/pgm-ExportReviewNotes.txt
index 17cc862..1b00213 100644
--- a/Documentation/pgm-ExportReviewNotes.txt
+++ b/Documentation/pgm-ExportReviewNotes.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-ExportReviewNotes - Export successful reviews to refs/notes/review
+ExportReviewNotes - Export successful reviews to link:refs-notes-review.html[refs/notes/review]
 
 SYNOPSIS
 --------
@@ -14,7 +14,7 @@
 -----------
 Scans every submitted change and creates an initial notes
 branch detailing the previous submission information for
-each merged changed.
+each merged change.
 
 This task can take quite some time, but can run in the background
 concurrently to the server if the database is MySQL or PostgreSQL.
diff --git a/Documentation/pgm-init.txt b/Documentation/pgm-init.txt
index c53c57d..57decdd 100644
--- a/Documentation/pgm-init.txt
+++ b/Documentation/pgm-init.txt
@@ -19,7 +19,7 @@
 for some basic setup prior to writing default configuration files
 into a newly created `$site_path`.
 
-If run an an existing `$site_path`, init will upgrade some resources
+If run in an existing `$site_path`, init will upgrade some resources
 as necessary.
 
 OPTIONS
@@ -28,6 +28,11 @@
 	Run in batch mode, skipping interactive prompts.  Reasonable
 	configuration defaults are chosen based on the whims of
 	the Gerrit developers.
++
+If during a schema migration unused objects (e.g. tables, columns)
+are detected they are *not* automatically dropped, but only a list of
+SQL statements to drop these objects is provided. To drop the unused
+objects these SQL statements have to be executed manually.
 
 \--no-auto-start::
 	Don't automatically start the daemon after initializing a
diff --git a/Documentation/prolog-change-facts.txt b/Documentation/prolog-change-facts.txt
new file mode 100644
index 0000000..d5f6174
--- /dev/null
+++ b/Documentation/prolog-change-facts.txt
@@ -0,0 +1,100 @@
+Prolog Facts for Gerrit Changes
+===============================
+
+Prior to invoking the `submit_rule(X)` query for a change, Gerrit initializes
+the Prolog engine with a set of facts (current data) about this change.
+The following table provides an overview of the provided facts.
+
+IMPORTANT: All the terms listed below are defined in the `gerrit` package. To use any
+of them we must use a qualified name like `gerrit:change_branch(X)`.
+
+.Prolog facts about the current change
+[grid="cols"]
+[options="header"]
+|=============================================================================
+|Fact                 |Example  |Description
+
+|`change_branch/1`    |`change_branch('refs/heads/master').`
+    |Destination Branch for the change as string atom
+
+|`change_owner/1`     |`change_owner(user(1000000)).`
+    |Owner of the change as `user(ID)` term. ID is the numeric account ID
+
+|`change_project/1`   |`change_project('full/project/name').`
+    |Name of the project as string atom
+
+|`change_topic/1`     |`change_topic('plugins').`
+    |Topic name as string atom
+
+|`commit_author/1`    |`commit_author(user(100000)).`
+    |Author of the commit as `user(ID)` term. ID is the numeric account ID
+
+|`commit_author/3`    |`commit_author(user(100000), 'John Doe', 'john.doe@example.com').`
+    |ID, full name and the email of the commit author.  The full name and the
+    email are string atoms
+
+|`commit_committer/1` |`commit_committer()`
+    |Committer of the commit as `user(ID)` term. ID is the numeric account ID
+
+|`commit_committer/3` |`commit_committer()`
+    |ID, full name and the email of the commit committer. The full name and the
+    email are string atoms
+
+.2+|`commit_label/2`  |`commit_label(label('Code-Review', 2), user(1000000)).`
+    .2+|Set of votes on the last patch-set
+
+                      |`commit_label(label('Verified', -1), user(1000001)).`
+
+|`commit_message/1`   |`commit_message('Fix bug X').`
+    |Commit message as string atom
+
+.4+|`current_user/1`  |`current_user(user(1000000)).`
+    .4+|Current user as one of the four given possibilities
+
+                      |`current_user(user(anonymous)).`
+                      |`current_user(user(peer_daemon)).`
+                      |`current_user(user(replication)).`
+|=============================================================================
+
+In addition Gerrit provides a set of built-in helper predicates that can be used
+when implementing the `submit_rule` predicate. The most common ones are listed in
+the following table.
+
+.Built-in Prolog helper predicates
+[grid="cols"]
+[options="header"]
+|=============================================================================
+|Predicate                  |Example usage  |Description
+
+|`commit_delta/1`           |`commit_delta('\\.java$').`
+    |True if any file name from the last patch set matches the given regex.
+
+|`commit_delta/3`           |`commit_delta('\\.java$', T, P)`
+    |Returns the change type (via `T`) and path (via `P`), if the change type
+    is `rename`, it also returns the old path. If the change type is `rename`, it
+    returns a delete for old path and an add for new path. If the change type
+    is `copy`, an add is returned along with new path.
+
+    Possible values for the change type are the following symbols: `add`,
+    `modify`, `delete`, `rename`, `copy`
+
+|`commit_delta/4`           |`commit_delta('\\.java$', T, P, O)`
+    |Like `commit_delta/3` plus the old path (via `O`) if applicable.
+
+|`commit_edits/2`           |`commit_edits('/pom.xml$', 'dependency')`
+    |True if any of the files matched by the file name regex (first parameter)
+    have edited lines that match the regex in the second parameter. This
+    example will be true if there is a modification of a `pom.xml` file such
+    that an edited line contains or contained the string `'dependency'`.
+
+|`commit_message_matches/1` |`commit_message_matches('^Bug fix')`
+    |True if the commit message matches the given regex.
+
+|=============================================================================
+
+NOTE: for a complete list of built-in helpers read the `gerrit_common.pl` and
+all Java classes whose name matches `PRED_*.java` from Gerrit's source code.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/prolog-cookbook.txt b/Documentation/prolog-cookbook.txt
new file mode 100644
index 0000000..e660067
--- /dev/null
+++ b/Documentation/prolog-cookbook.txt
@@ -0,0 +1,750 @@
+Gerrit Code Review - Prolog Submit Rules Cookbook
+=================================================
+
+Submit Rule
+-----------
+A 'Submit Rule' in Gerrit is logic that defines when a change is submittable.
+By default, a change is submittable when it gets at least one
+highest vote in each voting category and has no lowest vote (aka veto vote) in
+any category.  Typically, this means that a change needs 'Code-Review+2',
+'Verified+1' and has neither 'Code-Review-2' nor 'Verified-1' to become
+submittable.
+
+While this rule is a good default, there are projects which need more
+flexibility for defining when a change is submittable.  In Gerrit, it is
+possible to use Prolog based rules to provide project specific submit rules and
+replace the default submit rules. Using Prolog based rules, project owners can
+define a set of criteria which must be fulfilled for a change to become
+submittable. For a change that is not submittable, the set of needed criteria
+is displayed in the Gerrit UI.
+
+NOTE: Loading and executing Prolog submit rules may be disabled by setting
+`rules.enabled=false` in the Gerrit config file (see
+link:config-gerrit.html#_a_id_rules_a_section_rules[rules section])
+
+link:https://groups.google.com/d/topic/repo-discuss/wJxTGhlHZMM/discussion[This
+discussion thread] explains why Prolog was chosen for the purpose of writing
+project specific submit rules.
+link:http://gerrit-documentation.googlecode.com/svn/ReleaseNotes/ReleaseNotes-2.2.2.html[Gerrit
+2.2.2 ReleaseNotes] introduces Prolog support in Gerrit.
+
+Prolog Language
+---------------
+This document is not a complete Prolog tutorial.
+link:http://en.wikipedia.org/wiki/Prolog[This Wikipedia page on Prolog] is a
+good starting point for learning the Prolog language. This document will only explain
+some elements of Prolog that are necessary to understand the provided examples.
+
+Prolog in Gerrit
+----------------
+Gerrit uses its own link:https://code.google.com/p/prolog-cafe/[fork] of the
+original link:http://kaminari.istc.kobe-u.ac.jp/PrologCafe/[prolog-cafe]
+project. Gerrit embeds the prolog-cafe library and can interpret Prolog programs at
+runtime.
+
+Interactive Prolog Cafe Shell
+-----------------------------
+For interactive testing and playing with Prolog, Gerrit provides the
+link:pgm-prolog-shell.html[prolog-shell] program which opens an interactive
+Prolog interpreter shell.
+
+NOTE: The interactive shell is just a prolog shell, it does not load
+a gerrit server environment and thus is not intended for xref:TestingSubmitRules[testing submit rules].
+
+SWI-Prolog
+----------
+Instead of using the link:pgm-prolog-shell.html[prolog-shell] program one can
+also use the link:http://www.swi-prolog.org/[SWI-Prolog] environment. It
+provides a better shell interface and a graphical source-level debugger.
+
+The rules.pl file
+-----------------
+This section explains how to create and edit project specific submit rules. How
+to actually write the submit rules is explained in the next section.
+
+Project specific submit rules are stored in the `rules.pl` file in the
+`refs/meta/config` branch of that project.  Therefore, we need to fetch and
+checkout the `refs/meta/config` branch in order to create or edit the `rules.pl`
+file:
+
+====
+  $ git fetch origin refs/meta/config:config
+  $ git checkout config
+  ... edit or create the rules.pl file
+  $ git add rules.pl
+  $ git commit -m "My submit rules"
+  $ git push origin HEAD:refs/meta/config
+====
+
+How to write submit rules
+-------------------------
+Whenever Gerrit needs to evaluate submit rules for a change `C` from project `P` it
+will first initialize the embedded Prolog interpreter by:
+
+* consulting a set of facts about the change `C`
+* consulting the `rules.pl` from the project `P`
+
+Conceptually we can imagine that Gerrit adds a set of facts about the change
+`C` on top of the `rules.pl` file and then consults it. The set of facts about
+the change `C` will look like:
+
+====
+  :- package gerrit.                                                   <1>
+
+  commit_author(user(1000000), 'John Doe', 'john.doe@example.com').    <2>
+  commit_committer(user(1000000), 'John Doe', 'john.doe@example.com'). <3>
+  commit_message('Add plugin support to Gerrit').                      <4>
+  ...
+====
+
+<1> Gerrit will provide its facts in a package named `gerrit`. This means we
+have to use qualified names when writing our code and referencing these facts.
+For example: `gerrit:commit_author(ID, N, M)`
+<2> user ID, full name and email address of the commit author
+<3> user ID, full name and email address of the commit committer
+<4> commit message
+
+A complete set of facts which Gerrit provides about the change is listed in the
+link:prolog-change-facts.html[Prolog Facts for Gerrit Change].
+
+By default, Gerrit will search for a `submit_rule/1` predicate in the `rules.pl`
+file, evaluate the `submit_rule(X)` and then inspect the value of `X` in order
+to decide whether the change is submittable or not and also to find the set of
+needed criteria for the change to become submittable. This means that Gerrit has an
+expectation on the format and value of the result of the `submit_rule` predicate
+which is expected to be a `submit` term of the following format:
+
+====
+  submit(label(label-name, status) [, label(label-name, status)]*)
+====
+
+where `label-name` is usually `'Code-Review'` or `'Verified'` but could also
+be any other string (see examples below). The `status` is one of:
+
+* `ok(user(ID))` or just `ok(_)` if user info is not important. This status is
+   used to tell that this label/category has been met.
+* `need(_)` is used to tell that this label/category is needed for change to
+   become submittable
+* `reject(user(ID))` or just `reject(_)`. This status is used to tell that label/category
+   is blocking change submission
+* `impossible(_)` is used when the logic knows that the change cannot be submitted as-is.
+   Administrative intervention is probably required. This is meant for cases
+   where the logic requires members of "FooEng" to score "Code-Review +2" on a
+   change, but nobody is in group "FooEng". It is to hint at permissions
+   misconfigurations.
+* `may(_)` allows expression of approval categories that are optional, i.e.
+   could either be set or unset without ever influencing whether the change
+   could be submitted.
+
+NOTE: For a change to be submittable all `label` terms contained in the returned
+`submit` term must have either `ok` or `may` status.
+
+IMPORTANT: Gerrit will let the Prolog engine continue searching for solutions of
+the `submit_rule(X)` query until it finds the first one where all labels in the
+return result have either status `ok` or `may` or there are no more solutions.
+If a solution where all labels have status `ok` is found then all previously
+found solutions are ignored. Otherwise, all labels names with status `need`
+from all solutions will be displayed in the UI indicating the set of conditions
+needed for the change to become submittable.
+
+Here some examples of possible return values from the `submit_rule` predicate:
+
+====
+  submit(label('Code-Review', ok(_)))                               <1>
+  submit(label('Code-Review', ok(_)), label('Verified', reject(_))) <2>
+  submit(label('Author-is-John-Doe', need(_))                       <3>
+====
+
+<1> label `'Code-Review'` is met. As there are no other labels in the
+    return result, the change is submittable.
+<2> label `'Verified'` is rejected. Change is not submittable.
+<3> label `'Author-is-John-Doe'` is needed for the change to become submittable.
+    Note that this tells nothing about how this criteria will be met. It is up
+    to the implementor of the `submit_rule` to return `label('Author-is-John-Doe',
+    ok(_))` when this criteria is met.  Most likely, it will have to match
+    against `gerrit:commit_author` in order to check if this criteria is met.
+    This will become clear through the examples below.
+
+Of course, when implementing the `submit_rule` we will use the facts about the
+change that are already provided by Gerrit.
+
+Another aspect of the return result from the `submit_rule` predicate is that
+Gerrit uses it to decide which set of labels to display on the change review
+screen for voting. If the return result contains label `'ABC'` and if the label
+`'ABC'` is one of the (global) voting categories then voting for the label
+`'ABC'` will be displayed. Otherwise, it is not displayed. Note that we don't
+need a (global) voting category for each label contained in the result of
+`submit_rule` predicate.  For example, the decision whether `'Author-is-John-Doe'`
+label is met will probably not be made by explicit voting but, instead, by
+inspecting the facts about the change.
+
+Submit Filter
+-------------
+Another mechanism of changing the default submit rules is to implement the
+`submit_filter/2` predicate. While Gerrit will search for the `submit_rule` only
+in the `rules.pl` file of the current project, the `submit_filter` will be
+searched for in the `rules.pl` of all parent projects of the current project,
+but not in the `rules.pl` of the current project. The search will start from the
+immediate parent of the current project, then in the parent project of that
+project and so on until, and including, the 'All-Projects' project.
+
+The purpose of the submit filter is, as its name says, to filter the results
+of the `submit_rule`. Therefore, the `submit_filter` predicate has two
+parameters:
+
+====
+  submit_filter(In, Out) :- ...
+====
+
+Gerrit will invoke `submit_filter` with the `In` parameter containing a `submit`
+structure produced by the `submit_rule` and will take the value of the `Out`
+parameter as the result.
+
+The `Out` value of a `submit_filter` will become the `In` value for the
+next `submit_filter` in the parent line. The value of the `Out` parameter
+of the top-most `submit_filter` is the final result of the submit rule that
+is used to decide whether a change is submittable or not.
+
+IMPORTANT: `submit_filter` is a mechanism for Gerrit administrators to implement
+and enforce submit rules that would apply to all projects while `submit_rule` is
+a mechanism for project owners to implement project specific submit rules.
+However, project owners who own several projects could also make use of
+`submit_filter` by using a common parent project for all their projects and
+implementing the `submit_filter` in this common parent project. This way they
+can avoid implementing the same `submit_rule` in all their projects.
+
+The following "drawing" illustrates the order of the invocation and the chaining
+of the results of the `submit_rule` and `submit_filter` predicates.
+
+====
+  All-Projects
+  ^   submit_filter(B, S) :- ...  <4>
+  |
+  Parent-3
+  ^   <no submit filter here>
+  |
+  Parent-2
+  ^   submit_filter(A, B) :- ...  <3>
+  |
+  Parent-1
+  ^   submit_filter(X, A) :- ...  <2>
+  |
+  MyProject
+      submit_rule(X) :- ...       <1>
+====
+
+<1> The `submit_rule` of `MyProject` is invoked first.
+<2> The result `X` is filtered through the `submit_filter` from the `Parent-1`
+project.
+<3> The result of `submit_filter` from `Parent-1` project is filtered by the
+`submit_filter` in the `Parent-2` project. Since `Parent-3` project doesn't have
+a `submit_filter` it is skipped.
+<4> The result of `submit_filter` from `Parent-2` project is filtered by the
+`submit_filter` in the `All-Projects` project. The value in `S` is the final
+value of the submit rule evaluation.
+
+NOTE: If `MyProject` doesn't define its own `submit_rule` Gerrit will invoke the
+default implementation of submit rule that is named `gerrit:default_submit` and
+its result will be filtered as described above.
+
+[[TestingSubmitRules]]
+Testing submit rules
+--------------------
+The prolog environment running the `submit_rule` is loaded with state describing the
+change that is being evaluated. The easiest way to load this state is to test your
+`submit_rule` against a real change on a running gerrit instance. The command
+link:cmd-test-submit-rule.html[test-submit-rule] loads a specific change and executes
+the `submit_rule`. It optionally reads the rule from from `stdin` to facilitate easy testing.
+
+====
+  cat rules.pl | ssh gerrit_srv gerrit test-submit-rule I45e080b105a50a625cc8e1fb5b357c0bfabe6d68 -s
+====
+
+Prolog vs Gerrit plugin for project specific submit rules
+---------------------------------------------------------
+Since version 2.5 Gerrit supports plugins and extension points. A plugin or an
+extension point could also be used as another means to provide custom submit
+rules. One could ask for a guideline when to use Prolog based submit rules and
+when to go for writing a new plugin. Writing a Prolog program is usually much
+faster than writing a Gerrit plugin. Prolog based submit rules can be pushed
+to a project by project owners while Gerrit plugins could only be installed by
+Gerrit administrators. In addition, Prolog based submit rules can be pushed
+for review by pushing to `refs/for/refs/meta/config` branch.
+
+On the other hand, Prolog based submit rules get a limited amount of facts about
+the change exposed to them. Gerrit plugins get full access to Gerrit internals
+and can potentially check more things than Prolog based rules.
+
+Examples
+--------
+The following examples should serve as a cookbook for developing own submit rules.
+Some of them are too trivial to be used in production and their only purpose is
+to provide step by step introduction and understanding.
+
+Some of the examples will implement the `submit_rule` and some will implement
+the `submit_filter` just to show both possibilities.  Remember that
+`submit_rule` is only invoked from the current project and `submit_filter` is
+invoked from all parent projects. This is the most important fact in deciding
+whether to implement `submit_rule` or `submit_filter`.
+
+Example 1: Make every change submittable
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Let's start with a most trivial example where we would make every change submittable
+regardless of the votes it has:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(label('Any-Label-Name', ok(_)))).
+====
+
+In this case we make no use of facts about the change. We don't need it as we are simply
+making every change submittable. Note that, in this case, the Gerrit UI will not show
+the UI for voting for the standard `'Code-Review'` and `'Verified'` categories as labels
+with these names are not part of the return result. The `'Any-Label-Name'` could really
+be any string.
+
+Example 2: Every change submittable and voting in the standard categories possible
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This is continuation of the previous example where, in addition, to making
+every change submittable we want to enable voting in the standard
+`'Code-Review'` and `'Verified'` categories.
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(label('Code-Review', ok(_)), label('Verified', ok(_)))).
+====
+
+Since for every change all label statuses are `'ok'` every change will be submittable.
+Voting in the standard labels will be shown in the UI as the standard label names are
+included in the return result.
+
+Example 3: Nothing is submittable
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This example shows how to make all changes non-submittable regardless of the
+votes they have.
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(label('Any-Label-Name', reject(_)))).
+====
+
+Since for any change we return only one label with status `reject`, no change
+will be submittable. The UI will, however, not indicate what is needed for a
+change to become submittable as we return no labels with status `need`.
+
+Example 4: Nothing is submittable but UI shows several 'Need ...' criteria
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+In this example no change is submittable but here we show how to present 'Need
+<label>' information to the user in the UI.
+
+.rules.pl
+[caption=""]
+====
+  % In the UI this will show: Need Any-Label-Name
+  submit_rule(submit(label('Any-Label-Name', need(_)))).
+
+  % We could define more "need" labels by adding more rules
+  submit_rule(submit(label('Another-Label-Name', need(_)))).
+
+  % or by providing more than one need label in the same rule
+  submit_rule(submit(label('X-Label-Name', need(_)), label('Y-Label-Name', need(_)))).
+====
+
+In the UI this will show:
+****
+* Need Any-Label-Name
+* Need Another-Label-Name
+* Need X-Label-Name
+* Need Y-Label-Name
+****
+
+From the example above we can see a few more things:
+
+* comment in Prolog starts with the `%` character
+* there could be multiple `submit_rule` predicates. Since Prolog, by default, tries to find
+  all solutions for a query, the result will be union of all solutions.
+  Therefore, we see all 4 `need` labels in the UI.
+
+Example 5: The 'Need ...' labels not shown when change is submittable
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This example shows that, when there is a solution for `submit_rule(X)` where all labels
+have status `ok` then Gerrit will not show any labels with the `need` status from
+any of the previous `submit_rule(X)` solutions.
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(label('Some-Condition', need(_))).
+  submit_rule(label('Another-Condition', ok(_))).
+====
+
+The 'Need Some-Condition' will not be show in the UI because of the result of
+the second rule.
+
+The same is valid if the two rules are swapped:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(label('Another-Condition', ok(_))).
+  submit_rule(label('Some-Condition', need(_))).
+====
+
+The result of the first rule will stop search for any further solutions.
+
+Example 6: Make change submittable if commit author is "John Doe"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This is the first example where we will use the Prolog facts about a change that
+are automatically exposed by Gerrit. Our goal is to make any change submittable
+when the commit author is named `'John Doe'`. In the very first
+step let's make sure Gerrit UI shows 'Need Author-is-John-Doe' in
+the UI to clearly indicate to the user what is needed for a change to become
+submittable:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(label('Author-is-John-Doe', need(_)))).
+====
+
+This will show:
+****
+* Need Author-is-John-Doe
+****
+
+in the UI but no change will be submittable yet. Let's add another rule:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(label('Author-is-John-Doe', need(_)))).
+  submit_rule(submit(label('Author-is-John-Doe', ok(_))))
+    :- gerrit:commit_author(_, 'John Doe', _).
+====
+
+In the second rule we return `ok` status for the `'Author-is-John-Doe'` label
+if there is a `commit_author` fact where the full name is `'John Doe'`. If
+author of a change is `'John Doe'` then the second rule will return a solution
+where all labels have `ok` status and the change will become submittable. If
+author of a change is not `'John Doe'` then only the first rule will produce a
+solution. The UI will show 'Need Author-is-John-Doe' but, as expected, the
+change will not be submittable.
+
+Instead of checking by full name we could also check by the email address:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(label('Author-is-John-Doe', need(_)))).
+  submit_rule(submit(label('Author-is-John-Doe', ok(_))))
+    :- gerrit:commit_author(_, _, 'john.doe@example.com').
+====
+
+or by user id (assuming it is 1000000):
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(label('Author-is-John-Doe', need(_)))).
+  submit_rule(submit(label('Author-is-John-Doe', ok(_))))
+    :- gerrit:commit_author(user(1000000), _, _).
+====
+
+or by a combination of these 3 attributes:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(label('Author-is-John-Doe', need(_)))).
+  submit_rule(submit(label('Author-is-John-Doe', ok(_))))
+    :- gerrit:commit_author(_, 'John Doe', 'john.doe@example.com').
+====
+
+Example 7: Make change submittable if commit message starts with "Trivial fix"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Besides showing how to make use of the commit message text the purpose of this
+example is also to show how to match only a part of a string symbol. Similarly
+like commit author the commit message is provided as a string symbol which is
+an atom in Prolog terms. When working with an atom we could only match against
+the whole value. To match only part of a string symbol we have, at least, two
+options:
+
+* convert the string symbol into a list of characters and then perform
+  the "classical" list matching
+* use the `regex_matches/2` or, even more convenient, the
+  `gerrit:commit_message_matches/1` predicate
+
+Let's implement both options:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(label('Commit-Message-starts-with-Trivial-Fix', need(_)))).
+  submit_rule(submit(label('Commit-Message-starts-with-Trivial-Fix', ok(_))))
+    :- gerrit:commit_message(M), name(M, L), starts_with(L, "Trivial Fix").
+
+  starts_with(L, []).
+  starts_with([H|T1], [H|T2]) :- starts_with(T1, T2).
+====
+
+NOTE: The `name/2` embedded predicate is used to convert a string symbol into a
+list of characters. A string `abc` is converted into a list of characters `[97,
+98, 99]`.  A double quoted string in Prolog is just a shortcut for creating a
+list of characters. `"abc"` is a shortcut for `[97, 98, 99]`. This is why we use
+double quotes for the `"Trivial Fix"` in the example above.
+
+The `starts_with` predicate is self explaining.
+
+Using the `gerrit:commit_message_matches` predicate is probably more efficient:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(label('Commit-Message-starts-with-Trivial-Fix', need(_)))).
+  submit_rule(submit(label('Commit-Message-starts-with-Trivial-Fix', ok(_))))
+    :- gerrit:commit_message_matches('^Trivial Fix').
+====
+
+Reusing the default submit policy
+---------------------------------
+All examples until now concentrate on one particular aspect of change data.
+However, in real-life scenarios we would rather want to reuse Gerrit's default
+submit policy and extend/change it for our specific purpose. In other words, we
+would like to keep all the default policies (like the `Verified` category,
+vetoing change, etc...) and only extend/change an aspect of it. For example, we
+may want to disable the ability for change authors to approve their own changes
+but keep all other policies the same.
+
+To get results of Gerrits default submit policy we use the
+`gerrit:default_submit` predicate. This means that if we write a submit rule like:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(X) :- gerrit:default_submit(X).
+====
+
+then this is equivalent to not using `rules.pl` at all. We just delegate to
+default logic. However, once we invoke the `gerrit:default_submit(X)` we can
+perform further actions on the return result `X` and apply our specific
+logic. The following pattern illustrates this technique:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(S) :- gerrit:default_submit(R), project_specific_policy(R, S).
+
+  project_specific_policy(R, S) :- ...
+====
+
+The following examples build on top of the default submit policy.
+
+Example 8: Make change submittable only if `Code-Review+2` is given by a non author
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+In this example we introduce a new label `Non-Author-Code-Review` and make it
+satisfied if there is at least one `Code-Review+2` from a non author. All other
+default policies like the `Verified` category and vetoing changes still apply.
+
+First, we invoke `gerrit:default_submit` to compute the result for the default
+submit policy and then add the `Non-Author-Code-Review` label to it.  The
+`Non-Author-Code-Review` label is added with status `ok` if such an approval
+exists or with status `need` if it doesn't exist.
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(S) :-
+    gerrit:default_submit(X),
+    X =.. [submit | Ls],
+    add_non_author_approval(Ls, R),
+    S =.. [submit | R].
+
+  add_non_author_approval(S1, S2) :-
+    gerrit:commit_author(A), gerrit:commit_label(label('Code-Review', 2), R),
+    R \= A, !,
+    S2 = [label('Non-Author-Code-Review', ok(R)) | S1].
+  add_non_author_approval(S1, [label('Non-Author-Code-Review', need(_)) | S1]).
+====
+
+This example uses the `univ` operator `=..` to "unpack" the result of the
+default_submit, which is a structure of the form `submit(label('Code-Review',
+ok(_)), label('Verified', need(_)) ...)` into a list like `[submit,
+label('Code-Review', ok(_)), label('Verified', need(_)), ...]`.  Then we
+process the tail of the list (the list of labels) as a Prolog list, which is
+much easier than processing a structure. In the end we use the same `univ`
+operator to convert the resulting list of labels back into a `submit` structure
+which is expected as a return result. The `univ` operator works both ways.
+
+In `add_non_author_approval` we use the `cut` operator `!` to prevent Prolog
+from searching for more solutions once the `cut` point is reached. This is
+important because in the second `add_non_author_approval` rule we just add the
+`label('Non-Author-Code-Review', need(_))` without first checking that there
+is no non author `Code-Review+2`. The second rule will only be reached
+if the `cut` in the first rule is not reached and it only happens if a
+predicate before the `cut` fails.
+
+Example 9: Remove the `Verified` category
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A project has no build and test. It consists of only text files and needs only
+code review.  We want to remove the `Verified` category from this project so
+that `Code-Review+2` is the only criteria for a change to become submittable.
+We also want the UI to not show the `Verified` category in the table with
+votes and on the voting screen.
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(S) :-
+    gerrit:default_submit(X),
+    X =.. [submit | Ls],
+    remove_verified_category(Ls, R),
+    S =.. [submit | R].
+
+  remove_verified_category([], []).
+  remove_verified_category([label('Verified', _) | T], R) :- remove_verified_category(T, R), !.
+  remove_verified_category([H|T], [H|R]) :- remove_verified_category(T, R).
+====
+
+Example 10: Combine examples 8 and 9
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+In this example we want to both remove the verified and have the four eyes
+principle.  This means we want a combination of examples 7 and 8.
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(S) :-
+    gerrit:default_submit(X),
+    X =.. [submit | Ls],
+    remove_verified_category(Ls, R1),
+    add_non_author_approval(R1, R),
+    S =.. [submit | R].
+====
+
+The `remove_verified_category` and `add_non_author_approval` predicates are the
+same as defined in the previous two examples.
+
+Example 11: Remove the `Verified` category from all projects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Example 9, implements `submit_rule` that removes the `Verified` category from
+one project. In this example we do the same but we want to remove the `Verified`
+category from all projects. This means we have to implement `submit_filter` and
+we have to do that in the `rules.pl` of the `All-Projects` project.
+
+.rules.pl
+[caption=""]
+====
+  submit_filter(In, Out) :-
+    In =.. [submit | Ls],
+    remove_verified_category(Ls, R),
+    Out =.. [submit | R].
+
+  remove_verified_category([], []).
+  remove_verified_category([label('Verified', _) | T], R) :-
+    remove_verified_category(T, R), !.
+  remove_verified_category([H|T], [H|R]) :- remove_verified_category(T, R).
+====
+
+Example 12: 1+1=2 Code-Review
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+In this example we introduce accumulative voting to determine if a change is
+submittable or not. We modify the standard Code-Review to be accumulative, and make the
+change submittable if the total score is 2 or higher.
+
+The code in this example is very similar to Example 8, with the addition of findall/3
+and gerrit:remove_label.
+The findall/3 embedded predicate is used to form a list of all objects that satisfy a
+specified Goal. In this example it is used to get a list of all the 'Code-Review' scores.
+gerrit:remove_label is a built-in helper that is implemented similarly to the
+'remove_verified_category' as seen in the previous example.
+
+.rules.pl
+[caption=""]
+====
+  sum_list([], 0).
+  sum_list([H | Rest], Sum) :- sum_list(Rest,Tmp), Sum is H + Tmp.
+
+  add_category_min_score(In, Category, Min,  P) :-
+    findall(X, gerrit:commit_label(label(Category,X),R),Z),
+    sum_list(Z, Sum),
+    Sum >= Min, !,
+    P = [label(Category,ok(R)) | In].
+
+  add_category_min_score(In, Category,Min,P) :-
+    P = [label(Category,need(Min)) | In].
+
+  submit_rule(S) :-
+    gerrit:default_submit(X),
+    X =.. [submit | Ls],
+    gerrit:remove_label(Ls,label('Code-Review',_),NoCR),
+    add_category_min_score(NoCR,'Code-Review', 2, Labels),
+    S =.. [submit | Labels].
+====
+
+Example 13: Master and apprentice
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The master and apprentice example allow you to specify a user (the `master`)
+that must approve all changes done by another user (the `apprentice`).
+
+The code first checks if the commit author is in the apprentice database.
+If the commit is done by an apprentice, it will check if there is a +2
+review by the associated `master`.
+
+.rules.pl
+[caption=""]
+====
+  % master_apprentice(Master, Apprentice).
+  % Extend this with appropriate user-id's for your master/apprentice setup.
+  master_apprentice(user(1000064), user(1000000)).
+
+  submit_rule(S) :-
+    gerrit:default_submit(In),
+    In =.. [submit | Ls],
+    add_apprentice_master(Ls, R),
+    S =.. [submit | R].
+
+  check_master_approval(S1, S2, Master) :-
+    gerrit:commit_label(label('Code-Review', 2), R),
+    R = Master, !,
+    S2 = [label('Master-Approval', ok(R)) | S1].
+  check_master_approval(S1, [label('Master-Approval', need(_)) | S1], _).
+
+  add_apprentice_master(S1, S2) :-
+    gerrit:commit_author(Id),
+    master_apprentice(Master, Id),
+    !,
+    check_master_approval(S1, S2, Master).
+
+  add_apprentice_master(S, S).
+====
+
+Example 14: Only allow Author to submit change
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This example adds a new needed category `Patchset-Author` for any user that is
+not the author of the patch. This effectively blocks all users except the author
+from submitting the change. This could result in an impossible situation if the
+author does not have permissions for submitting the change.
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(S) :-
+    gerrit:default_submit(In),
+    In =.. [submit | Ls],
+    only_allow_author_to_submit(Ls, R),
+    S =.. [submit | R].
+
+  only_allow_author_to_submit(S, S) :-
+    gerrit:commit_author(Id),
+    gerrit:current_user(Id),
+    !.
+
+  only_allow_author_to_submit(S1, [label('Patchset-Author', need(_)) | S1]).
+====
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/refs-notes-review.txt b/Documentation/refs-notes-review.txt
new file mode 100644
index 0000000..632f567
--- /dev/null
+++ b/Documentation/refs-notes-review.txt
@@ -0,0 +1,111 @@
+The refs/notes/review namespace
+===============================
+
+Summary
+-------
+
+`refs/notes/review` is a special reference that Gerrit creates on repositories
+to store information about code reviews.
+
+When a repository is cloned from Gerrit, the `refs/notes/review` reference is
+not included by default.  It has to be manually fetched:
+
+====
+  $ git fetch origin refs/notes/review:refs/notes/review
+====
+
+It is also possible to
+link:http://www.kernel.org/pub/software/scm/git/docs/git-config.html[configure git]
+to always fetch `refs/notes/review`:
+
+====
+  $ git config --add remote.origin.fetch refs/notes/review:refs/notes/review
+  $ git fetch
+====
+
+When `refs/notes/review` is fetched on a repository, the Gerrit review
+information can be included in the git log output:
+
+====
+   $ git log --show-notes=review
+====
+
+Content of refs/notes/review
+----------------------------
+
+For each commit, Gerrit stores the following review information in
+`refs/notes/review`:
+
+[[submitted_by]]
+Submitted-by
+~~~~~~~~~~~~
+
+The name and email address of the Gerrit user that submitted the change in
+link:http://www.ietf.org/rfc/rfc2822.txt[RFC 2822] format.
+
+====
+  Submitted-by: Random J Developer <random@developer.example.org>
+====
+
+[[submitted_at]]
+Submitted-at
+~~~~~~~~~~~~
+
+The time the commit was submitted in RFC 2822 time stamp format.
+
+====
+  Submitted-at: Mon, 25 Jun 2012 16:15:57 +0200
+====
+
+[[reviewed_on]]
+Reviewed-on
+~~~~~~~~~~~
+
+The URL to the change on the Gerrit server.
+
+====
+  Reviewed-on: http://path.to.gerrit/12345
+====
+
+[[review_scores]]
+Review Labels and Scores
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Review label and score, and the name and email address of the Gerrit user that
+gave it in RFC 2822 format:
+
+====
+  Code-Review+2: A. N. Other <another@developer.example.org>
+  Verified+1: A. N. Other <another@developer.example.org>
+====
+
+Commonly used review labels are "Code-Review" and "Verified", but any label
+configured in Gerrit can be included.
+
+All review labels and scores present on the change at the time of submit are
+included.
+
+[[project]]
+Project
+~~~~~~~
+
+The name of the project in which the commit was made.
+
+====
+  Project: kernel/common
+====
+
+[[branch]]
+Branch
+~~~~~~
+
+The name of the branch on which the commit was made.
+
+====
+  Branch: refs/heads/master
+====
+
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/rest-api.txt b/Documentation/rest-api.txt
new file mode 100644
index 0000000..2f9d03f
--- /dev/null
+++ b/Documentation/rest-api.txt
@@ -0,0 +1,393 @@
+Gerrit Code Review - REST API
+=============================
+
+Gerrit Code Review comes with a REST like API available over HTTP.
+The API is suitable for automated tools to build upon, as well as
+supporting some ad-hoc scripting use cases.
+
+Protocol Details
+----------------
+
+[[authentication]]
+Authentication
+~~~~~~~~~~~~~~
+By default all REST endpoints assume anonymous access and filter
+results to correspond to what anonymous users can read (which may
+be nothing at all).
+
+Users (and programs) may authenticate using HTTP authentication by
+supplying the HTTP password from the user's account settings page.
+Gerrit by default uses HTTP digest authentication. To authenticate,
+prefix the endpoint URL with `/a/`. For example to authenticate to
+`/projects/` request URL `/a/projects/`.
+
+[[output]]
+Output Format
+~~~~~~~~~~~~~
+Most APIs return text format by default. JSON can be requested
+by setting the `Accept` HTTP request header to include
+`application/json`, for example:
+
+----
+  GET /projects/ HTTP/1.0
+  Accept: application/json
+----
+
+JSON responses are encoded using UTF-8 and use content type
+`application/json`. The JSON response body starts with a magic prefix
+line that must be stripped before feeding the rest of the response
+body to a JSON parser:
+
+----
+  )]}'
+  [ ... valid JSON ... ]
+----
+
+The default JSON format is `JSON_COMPACT`, which skips unnecessary
+whitespace. This is not the easiest format for a human to read. Many
+examples in this documentation use `format=JSON` as a query parameter
+to obtain pretty formatting in the response. Producing (and parsing)
+the compact format is more efficient, so most tools should prefer the
+default compact format.
+
+Responses will be gzip compressed by the server if the HTTP
+`Accept-Encoding` request header is set to `gzip`. This may
+save on network transfer time for larger responses.
+
+Endpoints
+---------
+
+[[accounts_self_capabilities]]
+/accounts/self/capabilities (Account Capabilities)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Returns the global capabilities (such as `createProject` or
+`createGroup`) that are enabled for the calling user. This can be used
+by UI tools to discover if administrative features are available
+to the caller, so they can hide (or show) relevant UI actions.
+
+----
+  GET /accounts/self/capabilities?format=JSON HTTP/1.0
+
+  )]}'
+  {
+    "queryLimit": {
+      "min": 0,
+      "max": 500
+    }
+  }
+----
+
+Administrator that has authenticated with digest authentication:
+----
+  GET /a/accounts/self/capabilities?format=JSON HTTP/1.0
+  Authorization: Digest username="admin", realm="Gerrit Code Review", nonce="...
+
+  )]}'
+  {
+    "administrateServer": true,
+    "queryLimit": {
+      "min": 0,
+      "max": 500
+    },
+    "createAccount": true,
+    "createGroup": true,
+    "createProject": true,
+    "killTask": true,
+    "viewCaches": true,
+    "flushCaches": true,
+    "viewConnections": true,
+    "viewQueue": true,
+    "startReplication": true
+  }
+----
+
+To filter the set of global capabilities the `q` parameter can be used.
+Filtering may decrease the response time by avoiding looking at every
+possible alternative for the caller.
+
+----
+  GET /a/accounts/self/capabilities?format=JSON&q=createAccount&q=createGroup HTTP/1.0
+  Authorization: Digest username="admin", realm="Gerrit Code Review", nonce="...
+
+  )]}'
+  {
+    "createAccount": true,
+    "createGroup": true
+  }
+----
+
+Most results are boolean, and a field is only present when its value
+is `true`. link:json.html#queryLimit[`queryLimit`] is a range and is
+presented as a nested JSON object with `min` and `max` members.
+
+[[projects]]
+/projects/ (List Projects)
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+Lists the projects accessible by the caller. This is the same as
+using the link:cmd-ls-projects.html[ls-projects] command over SSH,
+and accepts the same options as query parameters.
+
+----
+  GET /projects/?format=JSON&d HTTP/1.0
+
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json;charset=UTF-8
+   
+  )]}'
+  {
+    "external/bison": {
+      "description": "GNU parser generator"
+    },
+    "external/gcc": {},
+    "external/openssl": {
+      "description": "encryption\ncrypto routines"
+    },
+    "test": {
+      "description": "\u003chtml\u003e is escaped"
+    }
+  }
+----
+
+[[suggest-projects]]
+The `/projects/` URL also accepts a prefix string as part of the URL.
+This limits the results to those projects that start with the specified
+prefix.
+List all projects that start with `platform/`:
+----
+GET /projects/platform/?format=JSON HTTP/1.0
+HTTP/1.1 200 OK
+Content-Disposition: attachment
+Content-Type: application/json;charset=UTF-8
+)]}'
+{
+"platform/drivers": {},
+"platform/tools": {}
+}
+----
+E.g. this feature can be used by suggestion client UI's to limit results.
+
+[[changes]]
+/changes/ (Query Changes)
+~~~~~~~~~~~~~~~~~~~~~~~~~
+Queries changes visible to the caller. The query string must be
+provided by the `q` parameter. The `n` parameter can be used to limit
+the returned results.
+
+Query for open changes of watched projects:
+----
+  GET /changes/?format=JSON&q=status:open+is:watched&n=2 HTTP/1.0
+
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json;charset=UTF-8
+
+  )]}'
+  {
+    "project": "demo",
+    "branch": "master",
+    "id": "Idaf5e098d70898b7119f6f4af5a6c13343d64b57",
+    "subject": "One change",
+    "status": "NEW",
+    "created": "2012-07-17 07:18:30.854000000",
+    "updated": "2012-07-17 07:19:27.766000000",
+    "reviewed": true,
+    "_sortkey": "001e7057000006dc",
+    "_number": 1756,
+    "owner": {
+      "name": "John Doe"
+    },
+  },
+  {
+    "project": "demo",
+    "branch": "master",
+    "id": "I09c8041b5867d5b33170316e2abc34b79bbb8501",
+    "subject": "Another change",
+    "status": "NEW",
+    "created": "2012-07-17 07:18:30.884000000",
+    "updated": "2012-07-17 07:18:30.885000000",
+    "_sortkey": "001e7056000006dd",
+    "_number": 1757,
+    "owner": {
+      "name": "John Doe"
+    },
+    "_more_changes": true
+  }
+----
+
+The change output is sorted by the last update time, most recently
+updated to oldest update.
+
+If the `n` query parameter is supplied and additional changes exist
+that match the query beyond the end, the last change object has a
+`_more_changes: true` JSON field set. Callers can resume a query with
+the `n` query parameter, supplying the last change's `_sortkey` field
+as the value. When going in the reverse direction with the `p` query
+parameter a `_more_changes: true` is put in the first change object if
+there are results *before* the first change returned.
+
+Clients are allowed to specify more than one query by setting the `q`
+parameter multiple times. In this case the result is an array of
+arrays, one per query in the same order the queries were given in.
+
+Query that retrieves changes for a user's dashboard:
+----
+  GET /changes/?format=JSON&q=is:open+owner:self&q=is:open+reviewer:self+-owner:self&q=is:closed+owner:self+limit:5&o=LABELS HTTP/1.0
+
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json;charset=UTF-8
+
+  )]}'
+  [
+    [
+      {
+        "project": "demo",
+        "branch": "master",
+        "id": "Idaf5e098d70898b7119f6f4af5a6c13343d64b57",
+        "subject": "One change",
+        "status": "NEW",
+        "created": "2012-07-17 07:18:30.854000000",
+        "updated": "2012-07-17 07:19:27.766000000",
+        "reviewed": true,
+        "_sortkey": "001e7057000006dc",
+        "_number": 1756,
+        "owner": {
+          "name": "John Doe"
+        },
+        "labels": {
+          "Verified": {},
+          "Code-Review": {}
+        }
+      }
+    ],
+    [],
+    []
+  ]
+----
+
+Additional fields can be obtained by adding `o` parameters, each
+option requires more database lookups and slows down the query
+response time to the client so they are generally disabled by
+default. Optional fields are:
+
+* `LABELS`: a summary of each label required for submit, and
+  approvers that have granted (or rejected) with that label.
+
+* `CURRENT_REVISION`: describe the current revision (patch set)
+  of the change, including the commit SHA-1 and URLs to fetch from.
+
+* `ALL_REVISIONS`: describe all revisions, not just current.
+
+* `CURRENT_COMMIT`: parse and output all header fields from the
+  commit object, including message. Only valid when the current
+  revision or all revisions are selected.
+
+* `ALL_COMMITS`: parse and output all header fields from the
+  output revisions. If only `CURRENT_REVISION` was requested
+  then only the current revision's commit data will be output.
+
+* `CURRENT_FILES`: list files modified by the commit, including
+  basic line counts inserted/deleted per file. Only valid when
+  the current revision or all revisions are selected.
+
+* `ALL_FILES`: list files modified by the commit, including
+  basic line counts inserted/deleted per file. If only the
+  `CURRENT_REVISION` was requested the only that commit's
+  modified files will be output.
+
+----
+  GET /changes/?q=97&format=JSON&o=CURRENT_REVISION&o=CURRENT_COMMIT&o=CURRENT_FILES HTTP/1.0
+
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json;charset=UTF-8
+
+  )]}'
+  [
+    {
+      "project": "gerrit",
+      "branch": "master",
+      "id": "I7ea46d2e2ee5c64c0d807677859cfb7d90b8966a",
+      "subject": "Use an EventBus to manage star icons",
+      "status": "NEW",
+      "created": "2012-04-25 00:52:25.580000000",
+      "updated": "2012-04-25 00:52:25.586000000",
+      "_sortkey": "001c9bf400000061",
+      "_number": 97,
+      "owner": {
+        "name": "Shawn Pearce"
+      },
+      "current_revision": "184ebe53805e102605d11f6b143486d15c23a09c",
+      "revisions": {
+        "184ebe53805e102605d11f6b143486d15c23a09c": {
+          "_number": 1,
+          "fetch": {
+            "git": {
+              "url": "git://localhost/gerrit",
+              "ref": "refs/changes/97/97/1"
+            },
+            "http": {
+              "url": "http://127.0.0.1:8080/gerrit",
+              "ref": "refs/changes/97/97/1"
+            }
+          },
+          "commit": {
+            "parents": [
+              {
+                "commit": "1eee2c9d8f352483781e772f35dc586a69ff5646",
+                "subject": "Migrate contributor agreements to All-Projects."
+              }
+            ],
+            "author": {
+              "name": "Shawn O. Pearce",
+              "email": "sop@google.com",
+              "date": "2012-04-24 18:08:08.000000000",
+              "tz": -420
+            },
+            "committer": {
+              "name": "Shawn O. Pearce",
+              "email": "sop@google.com",
+              "date": "2012-04-24 18:08:08.000000000",
+              "tz": -420
+            },
+            "subject": "Use an EventBus to manage star icons",
+            "message": "Use an EventBus to manage star icons\n\nImage widgets that need to ..."
+          },
+          "files": {
+            "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeCache.java": {
+              "lines_deleted": 8
+            },
+            "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDetailCache.java": {
+              "lines_inserted": 1
+            },
+            "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java": {
+              "lines_inserted": 11,
+              "lines_deleted": 19
+            },
+            "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java": {
+              "lines_inserted": 23,
+              "lines_deleted": 20
+            },
+            "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarCache.java": {
+              "status": "D",
+              "lines_deleted": 139
+            },
+            "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarredChanges.java": {
+              "status": "A",
+              "lines_inserted": 204
+            },
+            "gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java": {
+              "lines_deleted": 9
+            }
+          }
+        }
+      }
+    }
+  ]
+----
+
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/user-changeid.txt b/Documentation/user-changeid.txt
index 124ec31..a3015e1 100644
--- a/Documentation/user-changeid.txt
+++ b/Documentation/user-changeid.txt
@@ -4,7 +4,7 @@
 Description
 -----------
 
-Gerrit Code Review sometimes relies upon Change-Id lines in the
+Gerrit Code Review sometimes relies upon a Change-Id line at the
 bottom of a commit message to uniquely identify a change across all
 drafts of it.  By including a unique Change-Id in the commit message,
 Gerrit can automatically associate a new version of a change back
@@ -37,7 +37,7 @@
 the commit name, `29a6...`, as the change may have been amended or
 rebased to address reviewer comments since its initial inception.
 
-To avoid confusion with commit names, Change-Ids typically are with
+To avoid confusion with commit names, Change-Ids are typically prefixed with
 an uppercase `I`.
 
 Creation
@@ -46,11 +46,13 @@
 Gerrit Code Review provides a standard 'commit-msg' hook which
 can be installed in the local Git repository to automatically
 create and insert a unique Change-Id line during `git commit`.
-To install the hook, copy it from Gerrit's daemon:
+To install the hook, copy it from Gerrit's daemon by executing
+one of the following commands while being in the root directory
+of the local Git repository:
 
   $ scp -p -P 29418 john.doe@review.example.com:hooks/commit-msg .git/hooks/
 
-  $ curl http://review.example.com/tools/hooks/commit-msg
+  $ curl -o .git/hooks/commit-msg http://review.example.com/tools/hooks/commit-msg
 
 For more details, see link:cmd-hook-commit-msg.html[commit-msg].
 
@@ -125,7 +127,7 @@
 already uploaded to Gerrit Code Review, and thus has a corresponding
 change that reviewers have already examined and left comments on.
 If you aren't sure which lines Gerrit knows about, try copying and
-pasting the lines into the search box in the top-right.
+pasting the lines into the search box at the top-right of the web interface.
 
 If Gerrit already knows about more than one Change-Id, pick one
 to keep in the squashed commit message, and manually abandon the
diff --git a/Documentation/user-custom-dashboards.txt b/Documentation/user-custom-dashboards.txt
new file mode 100644
index 0000000..a015e4c
--- /dev/null
+++ b/Documentation/user-custom-dashboards.txt
@@ -0,0 +1,48 @@
+Gerrit Code Review - Custom Dashboards
+======================================
+
+Description
+-----------
+
+A custom dashboard is shown in a layout similar to the per-user
+dashboard, but the sections are entirely configured from the URL.
+Because of this custom dashboards are stateless on the server side.
+Users or projects can simply trade URLs using an external system like
+a project wiki, or site administrators can put the links into the
+site's `GerritHeader.html` or `GerritFooter.html`.
+
+Dashboards are available via URLs like:
+----
+  /#/dashboard/?title=Custom+View&To+Review=reviewer:john.doe@example.com&Pending+In+myproject=project:myproject+is:open
+----
+This opens a view showing the title "Custom View" with two sections,
+"To Review" and "Pending in myproject":
+----
+  Custom View
+
+  To Review
+
+    Results of `reviewer:john.doe@example.com`
+
+  Pending In myproject
+
+    Results of `project:myproject is:open`
+----
+
+The dashboard URLs are easy to configure. All keys and values in the
+URL are encoded as query parameters. Set the page and window title
+using an optional `title=Text` parameter.
+
+Each section's title is defined by the parameter name, the section
+display order is defined by the order the parameters appear in the
+URL, and the query results are defined by the parameter value. To
+limit the number of rows in a query use `limit:N`, otherwise the
+entire result set will be shown (up to the user's query limit).
+
+Parameters may be separated from each other using any of the following
+characters, as some users may find one more readable than another:
+`&` or `;` or `,`
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/user-notify.txt b/Documentation/user-notify.txt
new file mode 100644
index 0000000..ae3c2d09
--- /dev/null
+++ b/Documentation/user-notify.txt
@@ -0,0 +1,127 @@
+Gerrit Code Review - Email Notifications
+========================================
+
+Description
+-----------
+
+Gerrit can automatically notify users by email when new changes are
+uploaded for review, after comments have been posted on a change,
+or after the change has been submitted to a branch.
+
+User Level Settings
+-------------------
+
+Individual users can configure email subscriptions by editing
+watched projects through Settings > Watched Projects with the web UI.
+
+Specific projects may be watched, or the special project
+`All-Projects` can be watched to watch all projects that
+are visible to the user.
+
+Change search expressions can be used to filter change notifications
+to specific subsets, for example `branch:master` to only see changes
+proposed for the master branch.
+
+Project Level Settings
+----------------------
+
+Project owners and site administrators can configure project level
+notifications, enabling Gerrit Code Review to automatically send
+emails to team mailing lists, or groups of users. Project settings
+are stored inside of the `refs/meta/config` branch of each Git
+repository, and are placed inside of the `project.config` file.
+
+To edit the project level notify settings, ensure the project owner
+has Push permission already granted for the `refs/meta/config`
+branch. Consult link:access-control.html[access controls] for
+details on how access permissions work.
+
+Initialize a temporary Git repository to edit the configuration:
+====
+  mkdir cfg_dir
+  cd cfg_dir
+  git init
+====
+
+Download the existing configuration from Gerrit:
+====
+  git fetch ssh://localhost:29418/project refs/meta/config
+  git checkout FETCH_HEAD
+====
+
+Enable notifications to an email address by adding to
+`project.config`, this can be done using the `git config` command:
+====
+  git config -f project.config --add notify.team.email team-address@example.com
+  git config -f project.config --add notify.team.email paranoid-manager@example.com
+====
+
+Examining the project.config file with any text editor should show
+a new notify section describing the email addresses to deliver to:
+----
+  [notify "team"]
+  	email = team-address@example.com
+  	email = paranoid-manager@example.com
+----
+
+Each notify section within a single project.config file must have a
+unique name. The section name itself does not matter and may later
+appear in the web UI. Naming a section after the email address or
+group it delivers to is typical. Multiple sections can be specified
+if different filters are needed.
+
+Commit the configuration change, and push it back:
+====
+  git commit -a -m "Notify team-address@example.com of changes"
+  git push ssh://localhost:29418/project HEAD:refs/meta/config
+====
+
+[[notify.name.email]]notify.<name>.email::
++
+List of email addresses to send matching notifications to. Each
+email address should be placed on its own line.
++
+Internal groups within Gerrit Code Review can also be named using
+`group NAME` syntax. If this format is used the group's UUID must
+also appear in the corresponding `groups` file. Gerrit will expand
+the group membership and BCC all current users.
+
+[[notify.name.type]]notify.<name>.type::
++
+Types of notifications to send. If not specified, all notifications
+are sent.
++
+* `new_changes`: Only newly created changes.
+* `all_comments`: Only comments on existing changes.
+* `submitted_changes`: Only changes that have been submitted.
+* `all`: All notifications.
+
++
+Like email, this variable may be a list of options.
+
+[[notify.name.filter]]notify.<name>.filter::
++
+link:user-search.html[Change search expression] to match changes that
+should be sent to the emails named in this section. Within a Git-style
+configuration file double quotes around complex operator values may
+need to be escaped, e.g. `filter = branch:\"^(maint|stable)-.*\"`.
+
+When sending email to a bare email address in a notify block, Gerrit
+Code Review ignores read access controls and assumes the administrator
+has set the filtering options correctly. Project owners can implement
+security filtering by adding the `visibleto:groupname` predicate to
+the filter expression, for example:
+
+====
+  [notify "Developers"]
+  	email = team-address@example.com
+  	filter = visibleto:Developers
+====
+
+When sending email to an internal group, the internal group's read
+access is automatically checked by Gerrit and therefore does not
+need to use the `visibleto:` operator in the filter.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 3aafe8c..8a231fe 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -16,9 +16,10 @@
 |All > Open           | status:open '(or is:open)'
 |All > Merged         | status:merged
 |All > Abandoned      | status:abandoned
-|My > Dafts           | has:draft
+|My > Drafts          | is:draft
 |My > Watched Changes | status:open is:watched
 |My > Starred Changes | is:starred
+|My > Draft Comments  | has:draft
 |Open changes in Foo  | status:open project:Foo
 |=================================================
 
@@ -75,7 +76,8 @@
 [[owner]]
 owner:'USER'::
 +
-Changes originally submitted by 'USER'.
+Changes originally submitted by 'USER'. The special case of
+`owner:self` will find changes owned by the caller.
 
 [[ownerin]]
 ownerin:'GROUP'::
@@ -85,7 +87,9 @@
 [[reviewer]]
 reviewer:'USER'::
 +
-Changes that have been, or need to be, reviewed by 'USER'.
+Changes that have been, or need to be, reviewed by 'USER'. The
+special case of `reviewer:self` will find changes where the caller
+has been added as a reviewer.
 
 [[reviewerin]]
 reviewerin:'GROUP'::
@@ -145,7 +149,7 @@
 [[tr,bug]]
 tr:'ID', bug:'ID'::
 +
-Search for changes whose commit message contains 'ID' and matched
+Search for changes whose commit message contains 'ID' and matches
 one or more of the
 link:config-gerrit.html#trackingid[trackingid sections]
 in the server's configuration file.  This is typically used to
@@ -162,7 +166,7 @@
 [[message]]
 message:'MESSAGE'::
 +
-Changes that matches 'MESSAGE' arbitrary string in body commit messages.
+Changes that match 'MESSAGE' arbitrary string in the commit message body.
 
 [[file]]
 file:^'REGEX'::
@@ -213,9 +217,23 @@
 True if there is at least one non-zero score on the change, in any
 approval category, by any user.
 
+is:owner::
++
+True on any change where the current user is the change owner.
+Same as `owner:self`.
+
+is:reviewer::
++
+True on any change where the current user is a reviewer.
+Same as `reviewer:self`.
+
 is:open::
 +
-True if the change is other open or submitted, merge pending.
+True if the change is either open or submitted, merge pending.
+
+is:draft::
++
+True if the change is a draft.
 
 is:closed::
 +
@@ -228,7 +246,7 @@
 [[status]]
 status:open::
 +
-True if the change state is other 'review in progress' or 'submitted,
+True if the change state is either 'review in progress' or 'submitted,
 merge pending'.
 
 status:reviewed::
@@ -250,7 +268,7 @@
 
 status:abandoned::
 +
-Change has been abandoned by the change owner, or administrator.
+Change has been abandoned.
 
 
 Boolean Operators
@@ -286,7 +304,7 @@
 [[labels]]
 Labels
 ------
-Label operators can be used to match approval score given during
+Label operators can be used to match approval scores given during
 a code review.  The specific set of supported labels depends on
 the server configuration, however `CodeReview` and `Verified`
 are the default labels provided out of the box.
@@ -305,7 +323,7 @@
   of change list pages.  Example: `label:R` or `label:V`.
 
 A label name must be followed by a score, or an operator and a score.
-The easiest way to explain these are by example.
+The easiest way to explain this is by example.
 
 `label:CodeReview=2`::
 `label:CodeReview=+2`::
@@ -373,16 +391,20 @@
 starredby:'USER'::
 +
 Matches changes that have been starred by 'USER'.
+The special case `starredby:self` applies to the caller.
 
 watchedby:'USER'::
 +
 Matches changes that 'USER' has configured watch filters for.
+The special case `watchedby:self` applies to the caller.
 
 draftby:'USER'::
 +
-Matches changes that 'USER' has left unpublished drafts on.
+Matches changes that 'USER' has left unpublished draft comments on.
 Since the drafts are unpublished, it is not possible to see the
-draft text, or even how many drafts there are.
+draft text, or even how many drafts there are. The special case
+of `draftby:self` will find changes where the caller has created
+a draft comment.
 
 limit:'CNT'::
 +
diff --git a/Documentation/user-submodules.txt b/Documentation/user-submodules.txt
index 3d14437..cfaf3e9 100644
--- a/Documentation/user-submodules.txt
+++ b/Documentation/user-submodules.txt
@@ -18,7 +18,7 @@
 any gitlinks and .gitmodules file with required info) and if so,
 a new submodule subscription is registered.
 
-When a new commit of a registered submodule is merged, gerrit
+When a new commit of a registered submodule is merged, Gerrit
 automatically updates the subscribers to the submodule with a new
 commit having the updated gitlinks.
 
@@ -31,7 +31,7 @@
 in the official git submodule command documentation.
 
 Imagine a repository called 'super' and another one called 'a'.
-Also consider 'a' available in a running gerrit instance on "server".
+Also consider 'a' available in a running Gerrit instance on "server".
 With this feature, one could attach 'a' inside of 'super' repository
 at path 'a' by executing the following command when being inside
 'super':
@@ -86,12 +86,12 @@
 gitlinks/.gitmodules file.
 
 The branch field of a submodule section is a custom git submodule
-feature for gerrit use. One should always be sure to fill it in
+feature for Gerrit use. One should always be sure to fill it in
 editing .gitmodules file after adding submodules to a super project,
-if it is the intention to make use of the gerrit feature introduced here.
+if it is the intention to make use of the Gerrit feature introduced here.
 
 Any git submodules which are added and not have the branch field
-available in the .gitmodules file will not be subscribed by gerrit
+available in the .gitmodules file will not be subscribed by Gerrit
 to automatically update the superproject.
 
 Detecting and Subscribing Submodules
@@ -114,7 +114,7 @@
 
 Imagine a superproject called 'super' having a branch called 'dev'
 having subscribed to a submodule 'a' on a branch 'dev-of-a'. When a commit
-is merged in branch 'dev-of-a' of 'a' project, gerrit automatically
+is merged in branch 'dev-of-a' of 'a' project, Gerrit automatically
 creates a new commit on branch 'dev' of 'super' updating the gitlink
 to point to the just merged commit.
 
@@ -123,11 +123,11 @@
 
 Gerrit will automatically update only the superprojects that added
 the submodules of urls of the running server (the one described in
-the canonical web url value in gerrit configuration file).
+the canonical web url value in Gerrit configuration file).
 
 The Gerrit instance administrator group should always certify to
 provide the canonical web url value in its configuration file. Users
-should certify to use the url value of the running gerrit instance to
+should certify to use the url value of the running Gerrit instance to
 add/subscribe submodules.
 
 Removing Subscriptions
diff --git a/Documentation/user-upload.txt b/Documentation/user-upload.txt
index 8e05e72..6bdff7a 100644
--- a/Documentation/user-upload.txt
+++ b/Documentation/user-upload.txt
@@ -46,7 +46,7 @@
 
 [TIP]
 Users who frequently upload changes will also want to consider
-starting a `ssh-agent`, and adding their private key to the list
+starting an `ssh-agent`, and adding their private key to the list
 managed by the agent, to reduce the frequency of entering the
 key's passphrase.  Consult `man ssh-agent`, or your SSH client's
 documentation, for more details on configuration of the agent
@@ -57,7 +57,7 @@
 ~~~~~~~~~~~~~~~~~~~
 
 To verify your SSH key is working correctly, try using an SSH client
-to connect to Gerrit's SSHD port.  By default Gerrit is running on
+to connect to Gerrit's SSHD port.  By default Gerrit runs on
 port 29418, using the same hostname as the web server:
 
 ====
@@ -104,7 +104,7 @@
 Create Changes
 ~~~~~~~~~~~~~~
 
-To create new changes for review, simply push into the project's
+To create new changes for review, simply push to the project's
 magical `refs/for/'branch'` ref using any Git client tool:
 
 ====
@@ -316,7 +316,7 @@
 
 repo is a multiple repository management tool, most commonly
 used by the Android Open Source Project.  For more details, see
-link:http://source.android.com/download/using-repo[using repo].
+link:http://source.android.com/source/using-repo.html[using repo].
 
 [[repo_create]]
 Create Changes
diff --git a/ReleaseNotes/ReleaseNotes-2.2.2.txt b/ReleaseNotes/ReleaseNotes-2.2.2.txt
index 3f1f76f..ddfe323 100644
--- a/ReleaseNotes/ReleaseNotes-2.2.2.txt
+++ b/ReleaseNotes/ReleaseNotes-2.2.2.txt
@@ -33,7 +33,7 @@
 +
 Projects now inherit the prolog rules defined in their parent
 project. Submit results from the child project are filtered by the
-parent project using the filter predicate defined the parent's
+parent project using the filter predicate defined in the parent's
 rules.pl. The results of the filtering are then passed up to the
 parent's parent and filtered, repeating this process up to the top
 level All-Projects.
@@ -56,7 +56,7 @@
 * prolog-shell: Simple command line Prolog interpreter
 +
 Define a small interactive interpreter that users or site
-administartors can play around with by downloading the Gerrit WAR
+administrators can play around with by downloading the Gerrit WAR
 file and executing: java -jar gerrit.war prolog-shell
 
 Prolog Predicates
diff --git a/ReleaseNotes/ReleaseNotes-2.5.1.txt b/ReleaseNotes/ReleaseNotes-2.5.1.txt
new file mode 100644
index 0000000..ba4e204
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.5.1.txt
@@ -0,0 +1,94 @@
+Release notes for Gerrit 2.5.1
+==============================
+
+Gerrit 2.5.1 is now available:
+
+link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-full-2.5.1.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-full-2.5.1.war]
+
+There are no schema changes from 2.5, or 2.5.1.
+
+However, if upgrading from a version older than 2.5, follow the upgrade
+procedure in the 2.5 link:ReleaseNotes-2.5.html[Release Notes].
+
+Security Fixes
+--------------
+* Correctly identify Git-over-HTTP operations
++
+Git operations over HTTP should be classified as using AccessPath.GIT
+and not WEB_UI. This ensures RefControl will correctly test for Create,
+Push or Delete access on a reference instead of Owner.
++
+E.g. without this fix project owners are able to force push commits
+via HTTP that are already in the history of the target branch, even
+without having any Push access right assigned.
+
+* Make sure only Gerrit admins can change the parent of a project
++
+Only Gerrit administrators should be able to change the parent of a
+project because by changing the parent project access rights and BLOCK
+rules which are configured on a parent project can be avoided.
++
+The `set-project-parent` SSH command already verifies that the caller
+is a Gerrit administrator, however project owners can change the parent
+project by modifying the `project.config` file and pushing to the
+`refs/meta/config` branch.
++
+This fix ensures that changes to the `project.config` file that change
+the parent project can only be pushed/submitted by Gerrit
+administrators.
++
+In addition it is now no longer possible to
+- set a non-existing project as parent (as this would make the project
+  be orphaned)
+- set a parent project for the `All-Projects` root project (the root
+  project by definition has no parent)
+by pushing changes of the `project.config` file to `refs/meta/config`.
+
+Bug Fixes
+---------
+* Fix RequestCleanup bug with Git over HTTP
++
+Decide if a continuation is going to be used early, before the filter
+that will attempt to cleanup a RequestCleanup. If so don't allow
+entering the RequestCleanup part of the system until the request is
+actually going to be processed.
++
+This fixes the IllegalStateException `Request has already been cleaned
+up` that occurred when running on Jetty and pushing over HTTP for URLs
+where the path starts with `/p/`.
+
+* Match all git fetch/clone/push commands to the command executor
++
+Route not just `/p/` but any Git access to the same thread pool as the
+SSH server is using, allowing all requests to compete fairly for
+resources.
+
+* Fix auto closing of changes on direct push
++
+When a commit is directly pushed into a repository (bypassing code
+review) and this commit has a Change-Id in its commit message then the
+corresponding change is automatically closed if it is open.
+
+* Allow assigning `Push` for `refs/meta/config` on `All-Projects`
++
+The `refs/meta/config` branch of the `All-Projects project` should only
+be modified by Gerrit administrators because being able to do
+modifications on this branch means that the user could assign himself
+administrator permissions.
++
+In addition to being administrator we already require that the
+administrator has the `Push` access right for `refs/meta/config` in
+order to be able to modify it (just as with all other branches
+administrators do not have edit permissions by default).
++
+The problem was that assigning the `Push` access right for
+`refs/meta/config` on the `All-Projects` project was not allowed.
++
+Having the `Push` access right for `refs/meta/config` on the
+`All-Projects` project without being administrator already has no
+effect.
++
+Prohibiting to assign the Push access right for `refs/meta/config` on
+the `All-Project` project was anyway pointless since it was e.g.
+possible to assign the `Push` access right on `refs/meta/*`.
+
diff --git a/ReleaseNotes/ReleaseNotes-2.5.2.txt b/ReleaseNotes/ReleaseNotes-2.5.2.txt
new file mode 100644
index 0000000..ceb23c2
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.5.2.txt
@@ -0,0 +1,138 @@
+Release notes for Gerrit 2.5.2
+==============================
+
+Gerrit 2.5.2 is now available:
+
+link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-full-2.5.2.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-full-2.5.2.war]
+
+There are no schema changes from 2.5, or 2.5.1.
+
+However, if upgrading from a version older than 2.5, follow the upgrade
+procedure in the 2.5 link:ReleaseNotes-2.5.html[Release Notes].
+
+Bug Fixes
+---------
+* Improve performance of ReceiveCommits for repos with many refs
++
+When validating the received commits all existing refs were added as
+uninteresting to the RevWalk. This resulted in bad performance when a
+repository had many refs (>100000). Putting existing 'refs/changes/'
+or 'refs/tags/' into the RevWalk is now avoided, which improves the
+performance.
+
+* Improve Push performance by discarding 'cache-automerge/*' refs
+  early in VisibleRefFilter
++
+For a typical large Git repository, with many refs and lots of cached
+merges, the push time goes down significantly.
+
+* Don't display all files from a merge-commit when auto-merge fails
++
+For merge commits Gerrit shows the difference to the automatic merge
+result. The creation of the auto-merge result may fail, e.g. when the
+merge commit has multiple merge bases (because JGit doesn't support
+this case yet). In this case Gerrit was showing all files from the
+merge commit. This caused several issues:
++
+--
+** the file list was too large for projects with a large number of
+   files
+** Gerrit would send too many false notification emails to users
+   watching changes under certain paths
+** both client and server needed a lot of resources in order to handle
+   such a large list of files
+--
++
+Now the file list for a merge commit will be empty when the creation
+of the auto-merge result fails.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1726[issue 1726]:
+  Create ref for new patch set on direct push
++
+If a change is in review and a new commit that has the Change-Id of
+this change in its commit message is pushed directly, then a new patch
+set for this commit is created and the change gets automatically
+closed. The problem was that no change ref for this new patch set was
+created and as result the change ref that was shown for the new patch
+set in the WebUI, and which was contained in the patchset-created
+event, was invalid.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1767[issue 1767]:
+  Remove wrong error message when pushing a new ref fails
++
+If pushing a new ref was rejected because the user was not allowed to
+create it the error message always told the user that he's missing the
+'Create Reference' access right. This message was incorrect in some
+cases. Users that have the 'Create Reference' access right assigned
+are e.g. not allowed to create the ref if:
++
+--
+** they are pushing an annotated tag without having the
+   'Push Annotated Tag' access right
+** they are pushing a signed tag without having the 'Push Signed Tag'
+   access right
+** the project state is set to 'Read Only'
+--
++
+Now the error message just says 'Prohibited by Gerrit'. This generic
+error message is better than a more concrete error message which is
+wrong in same cases because a wrong message is misleading and
+confuses the user.
++
+In addition the description of the 'Prohibited by Gerrit' error in the
+documentation has been updated to explain some additional cases in
+which the 'Prohibited by Gerrit' error occurs.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1444[issue 1444]:
+  Remove 'Mailing-List' header from sent emails
++
+The non-standard 'Mailing-List' header that is included in the emails
+sent by Gerrit isn't allowed by the Amazon Simple Email Service and is
+now removed.
+
+* Improve SMTP client error messages
++
+The wording of the error messages in the SMTP client was changed to
+make it more clear at exactly what stage in the SMTP transaction the
+server returned an error. Also the server's response text is now
+always included.
++
+In addition it is now ensured that already rejected recipients are
+included in the error message when the server rejects the DATA
+command. Without this there is no way of debugging rejected
+recipients if all recipients are rejected since that typically
+results in a DATA command rejection. Because some SMTP servers (e.g.
+Postfix with the default configuration) delay rejection of HELO/EHLO
+and MAIL FROM commands to the RCPT TO stage, this can happen not only
+for bad recipients.
+
+* Allow time unit variables to be '0'
++
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html[
+Gerrit Configuration parameters] that expect a numerical time unit as
+value can now be set to '0'.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1076[issue 1076]:
+  Fix CLA hyperlink on account registration page
++
+The New Contributor Agreement hyperlink on the Account Registration page
+was malformed.
+
+* Fix broken link to repo command reference
++
+The link to the repo command reference in the 'repo upload' section of
+the 'Uploading Changes' documentation was broken.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1569[issue 1569]:
+Fix unexpected behaviour in the commit-msg hook caused by `GREP_OPTIONS`
++
+If `GREP_OPTIONS` was set, it caused unexpected behaviour in the
+commit-msg hook.  For example if it included a setting like
+`--exclude=".git/*"` it caused a new `Change-Id` line to be appended
+to the commit message on every amend.
++
+`GREP_OPTIONS` is now unset at the beginning of the commit-msg script
+to prevent such problems from occurring.
++
+The `GREP_OPTIONS` setting in the user's environment is unaffected
+by this change.
diff --git a/ReleaseNotes/ReleaseNotes-2.5.3.txt b/ReleaseNotes/ReleaseNotes-2.5.3.txt
new file mode 100644
index 0000000..60efa7a
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.5.3.txt
@@ -0,0 +1,22 @@
+Release notes for Gerrit 2.5.3
+==============================
+
+Gerrit 2.5.3 is now available:
+
+link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.5.3.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.5.3.war]
+
+There are no schema changes from any of the 2.5.x versions.
+
+However, if upgrading from a version older than 2.5, follow the upgrade
+procedure in the 2.5 link:ReleaseNotes-2.5.html[Release Notes].
+
+Security Fixes
+--------------
+* Patch vulnerabilities in OpenID client library
++
+Installations using OpenID for authentication were vulnerable to a
+number of attacks over the network.  The openid4java client library
+was identified as the entry point.  In this release Gerrit updated to
+the latest 0.9.8 release, which patches the known attack vectors.
+
+No other changes since 2.5.2.
diff --git a/ReleaseNotes/ReleaseNotes-2.5.4.txt b/ReleaseNotes/ReleaseNotes-2.5.4.txt
new file mode 100644
index 0000000..1657d9b
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.5.4.txt
@@ -0,0 +1,22 @@
+Release notes for Gerrit 2.5.4
+==============================
+
+Gerrit 2.5.4 is now available:
+
+link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.5.4.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.5.4.war]
+
+There are no schema changes from any of the 2.5.x versions.
+
+However, if upgrading from a version older than 2.5, follow the upgrade
+procedure in the 2.5 link:ReleaseNotes-2.5.html[Release Notes].
+
+Bug Fixes
+---------
+* Require preferred email to be verified
++
+Some users were able to select a preferred email address that was
+not previously verified. This may have allowed the server to send
+notifications to an invalid destination, resulting in higher than
+usual bounce rates.
+
+No other changes since 2.5.3.
diff --git a/ReleaseNotes/ReleaseNotes-2.5.txt b/ReleaseNotes/ReleaseNotes-2.5.txt
new file mode 100644
index 0000000..55fbb60
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.5.txt
@@ -0,0 +1,1950 @@
+Release notes for Gerrit 2.5
+============================
+
+Gerrit 2.5 is now available:
+
+link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-full-2.5.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-full-2.5.war]
+
+Gerrit 2.5 includes the bug fixes done with
+link:ReleaseNotes-2.4.1.html[Gerrit 2.4.1] and
+link:ReleaseNotes-2.4.2.html[Gerrit 2.4.2]. These bug fixes are *not*
+listed in these release notes.
+
+Schema Change
+-------------
+*WARNING:* This release contains schema changes.  To upgrade:
+----
+  java -jar gerrit.war init -d site_path
+----
+
+*WARNING:* Upgrading to 2.5.x requires the server be first upgraded to 2.1.7 (or
+a later 2.1.x version), and then to 2.5.x.  If you are upgrading from 2.2.x.x or
+newer, you may ignore this warning and upgrade directly to 2.5.x.
+
+Warning on upgrade to schema version 68
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The migration to schema version 68, may result in a warning, which can
+be ignored when running init in the interactive mode.
+
+E.g. this warning may look like this:
+
+----
+Upgrading database schema from version 67 to 68 ...
+warning: Cannot create index for submodule subscriptions
+Duplicate key name 'submodule_subscriptions_access_bySubscription'
+Ignore warning and proceed with schema upgrade [y/N]?
+----
+
+This migration is creating an index for the
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/user-submodules.html[submodule feature] in
+Gerrit. When the submodule feature was introduced the index was only
+created when a new site was initialized, but not when Gerrit was
+upgraded. This migration tries to create the index, but it will only
+succeed if the index does not exist yet. If the index exists already,
+the creation of the index will fail. There was no database independent
+way to detect this case and this is why this migration leaves it to the
+user to decide if a failure should be ignored or not. If from the error
+message you can see that the migration failed because the index exists
+already (as in the example above), you can safely ignore this warning.
+
+Upgrade Warnings
+----------------
+
+[[replication]]
+Replication
+~~~~~~~~~~~
+
+Gerrit 2.5 no longer includes replication support out of the box.
+Servers that reply upon `replication.config` to copy Git repository
+data to other locations must also install the replication plugin.
+
+Cache Configuration
+~~~~~~~~~~~~~~~~~~~
+
+Disk caches are now backed by individual H2 databases, rather than
+Ehcache's own private format. Administrators are encouraged to clear
+the `'$site_path'/cache` directory before starting the new server.
+
+The `cache.NAME.diskLimit` configuration variable is now expressed in
+bytes of disk used. This is a change from previous versions of Gerrit,
+which expressed the limit as the number of entries rather than bytes.
+Bytes of disk is a more accurate way to size what is held. Admins that
+set this variable must update their configurations, as the old values
+are too small. For example a setting of `diskLimit = 65535` will only
+store 64 KiB worth of data on disk and can no longer hold 65,000 patch
+sets. It is recommended to delete the diskLimit variable (if set) and
+rely on the built-in default of `128m`.
+
+The `cache.diff.memoryLimit` and `cache.diff_intraline.memoryLimit`
+configuration variables are now expressed in bytes of memory used,
+rather than number of entries in the cache. This is a change from
+previous versions of Gerrit and gives administrators more control over
+how memory is partioned within a server. Admins that set this variable
+must update their configurations, as the old values are too small.
+For example a setting of `memoryLimit = 1024` now means only 1 KiB of
+data (which may not even hold 1 patch set), not 1024 patch sets.  It
+is recommended to set these to `10m` for 10 MiB of memory, and
+increase as necessary.
+
+The `cache.NAME.maxAge` variable now means the maximum amount of time
+that can elapse between reads of the source data into the cache, no
+matter how often it is being accessed. In prior versions it meant how
+long an item could be held without being requested by a client before
+it was discarded. The new meaning of elapsed time before consulting
+the source data is more useful, as it enables a strict bound on how
+stale the cached data can be. This is especially useful for slave
+servers account and permission data, or the `ldap_groups` cache, where
+updates are often made to the source without telling Gerrit to reload
+the cache.
+
+New Features
+------------
+
+Plugins
+~~~~~~~
+
+The Gerrit server functionality can be extended by installing plugins.
+Depending on how tightly the extension code is coupled with the Gerrit
+server code, there is a distinction between
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#plugin[plugins] and
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#extension[extensions].
+
+* link:#replication[Move replication logic to replication plugin]
++
+This splits all of the replication code out of the core server
+and moves it into a standard plugin.
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html[Documentation about
+  plugin development] including instructions for:
+** link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#getting-started[how to get
+   started with plugin development]
+** link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#deployment[plugin
+   deployment/installation]
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#API[API for plugins and
+  extensions]
+
+* Support for link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#ssh[SSH command
+  plugins]
++
+Allows plugin developers to declare additional SSH commands.
+
+* Enable link:#ssh-alias[aliases for SSH commands]
++
+Site administrators can alias SSH commands from a plugin into the
+`gerrit` namespace.
++
+The aliases are configured statically at server startup, but are
+resolved dynamically at invocation time to the currently loaded
+version of the plugin. If the plugin is not loaded, or does not
+define the command, "not found" is returned to the user.
+
+* Support for link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#http[HTTP
+  plugins]
++
+Plugins may contribute to the /plugins/NAME/ URL space.
+
+* Automatic registration of plugin bindings
++
+If a plugin has no modules declared in the manifest, automatically
+generate the modules for the plugin based on the class files that
+appear in the plugin and the `@Export` annotations that appear on
+these concrete classes.
++
+For any non-abstract command that extends SshCommand, plugins may
+declare the command with `@Export("name")` to
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#ssh[bind the implementation
+as that SSH command].
++
+Likewise link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#http[HTTP servlets
+can also be bound to URLs].
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#data-directory[Support a data
+  directory for plugins on demand]
+
+* Support serving static/ and Documentation/ from plugins
++
+The static/ and Documentation/ resource directories of a plugin can be
+served over HTTP for any loaded and running plugin, even if it has no
+other HTTP handlers. This permits a plugin to supply icons or other
+graphics for the web UI, or documentation content to help users learn
+how to use the plugin.
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#documentation[Auto-formatting
+  of plugin HTTP pages from Markdown files]
++
+If Gerrit detects that a requested plugin resource does not exist, but
+instead a file with a `.md` extension does exist, Gerrit opens the
+`.md` file and reformats it as html.
+
+* Support of link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#macros[macros in
+  Markdown plugin documentation]
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#auto-index[Automatic
+  generation of an index for the plugin documentation]
+
+* Support for audit plugins
++
+Plugins can implement an `AuditListener` to be informed about auditable
+actions:
++
+----
+  @Listener
+  public class MyAuditTrail extends AuditListener
+----
++
+The plugin must define a plugin module that binds the implementation of
+the audit listener in the `configure()` method:
++
+----
+  DynamicSet.bind(binder(), AuditListener.class).to(MyAuditTrail.class);
+----
+
+* Web UI for plugins
++
+Administrators can see the list of installed plugins in the WebUI
+under `Admin` > `Plugins`. For each plugin the plugin status is shown
+and it is possible to navigate to the plugin documentation.
+
+* Servlet to list plugins
++
+Administrators can retrieve plugin information from a REST interface
+by loading `<server-url>/a/plugins/`.
+
+* Support SSH commands to
+** link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-plugin-ls.html[list the installed
+   plugins]
+** link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-plugin-install.html[install plugins]
+** link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-plugin-enable.html[enable plugins]
+** link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-plugin-remove.html[disable plugins]
+** link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-plugin-reload.html[reload plugins]
+
+* Support installation of core plugin on site initialization
+
+* Automatically load/unload/reload plugins
++
+The PluginScanner thread runs every 1 minute by default and loads any
+newly created plugins, unloads any deleted plugins, and reloads any
+plugins that have been modified.
++
+The check frequency can be configured by setting
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#plugins.checkFrequency[
+plugins.checkFrequency] in the Gerrit config file. By configuration
+the scanner can also be disabled.
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#classpath[Loading of plugins
+  in own ClassLoader]
+
+* Plugin cleanup in the background
++
+When a plugin is stopped, schedule a Plugin Cleaner task to run
+1 minute later to try and clean out the garbage and release the
+JAR from `$site_path/tmp`.
+
+* Export `LifecycleListener` as extension point
++
+Extensions may need to know when they are starting or stopping.
+Export the interface that they can use to learn this information.
+
+* Support injection of `ServerInformation` into extensions and plugins
++
+Plugins can take this value by injection and learn the current
+server state during their own LifecycleListener. This enables a
+plugin to determine if it is loading as part of server startup, or
+because it was dynamically installed or reloaded by an administrator.
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-plugins.html#getting-started[Maven
+  archetype for creating gerrit plugin projects]
+
+* Enables the use of session management in Jetty
++
+This enables plugins to make use of servlet sessions.
+
+REST API
+~~~~~~~~
+Gerrit now supports a REST like API available over HTTP. The API is
+suitable for automated tools to build upon, as well as supporting some
+ad-hoc scripting use cases.
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/rest-api.html[Documentation of the REST API]
+
+* Support REST endpoints to
+** link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/rest-api.html#changes[query changes]
+** link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/rest-api.html#projects[list projects]
+** link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/rest-api.html#suggest-projects[suggest
+   projects]
+** link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/rest-api.html#accounts_self_capabilities[query
+   the global capabilities of the calling user]
+
+* Support link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/rest-api.html#authentication[anonymous
+  and authenticated access] to the REST endpoints
+
+* Support link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/rest-api.html#output[JSON output
+  format] for the REST endpoints
+
+The new REST API is used from the Gerrit WebUI.
+
+Some of the methods from the old internal JSON-RPC interface were
+completely replaced by the new REST API and got deleted:
+
+* `ProjectAdminService.visibleProjects(AsyncCallback<ProjectList>)`
+* `ProjectAdminService.suggestParentCandidates(AsyncCallback<List<Project>>)`
+* `ChangeListService.myStarredChangeIds(AsyncCallback<Set<Change.Id>>)`
+* `ChangeListService.allQueryNext(String, String, int, AsyncCallback<SingleListChangeInfo>)`
+* `ChangeListService.allQueryPrev(String, String, int, AsyncCallback<SingleListChangeInfo>)`
+* `ChangeListService.forAccount(Account.Id, AsyncCallback<AccountDashboardInfo>)`
+
+[[query-deprecation]]
+In addition the `/query` API has been deprecated. By default it is
+still available but server administrators may disable it by setting
+the link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#site.enableDeprecatedQuery[
+`site.enableDeprecatedQuery`] parameter in the Gerrit config file. This
+allows to enforce tools to move to the new API.
+
+Web
+~~~
+
+Change Screen
+^^^^^^^^^^^^^
+
+* Display commit message in a box
++
+The commit message on the change screen is now placed in a box with a
+title and emphasis on the commit summary. The star icon and the
+permalink are displayed in the box header. The header from the change
+screen is removed as it only held duplicate information.
+
+* Open the dependency section automatically when the change is needed
+  by an open change
+
+* Only show a change as needed by if its current patch set depends on
+  the change
+
+* Show only changes of the same project in the 'Depends On' section
++
+If two projects share the same history it can happen that the same
+commit is pushed for both projects, resulting in two changes. If now
+a successor commit is pushed for one of the projects, the resulting
+successor change was wrongly listing both changes in the 'Depends On'
+section. Now only the predecessor change of the own project is listed.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1383[issue 1383]:
+  Display the approval table on the PublishCommentsScreen.
++
+So far the approval table that shows the reviewers and their current
+votes was only shown on the ChangeScreen. Now it is also shown on the
+PublishCommentScreen. This allows the reviewer to see all existing
+votes and reviewers when doing their own voting and publishing of
+comments. Seeing the existing votes helps the reviewer in
+understanding which votes are still required before the change can be
+submitted.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1380[issue 1380]:
+  Display time next to change comments
++
+When a comment was posted yesterday, or any time older than 1 day but
+less than 1 year ago, display the time too. Display "May 2 17:37" rather
+than just "May 2".
+
+* Only show "Can Merge" when the change is new or draft
+
+* Allow auto suggesting reviewers to draft changes
++
+Auto completing users for draft changes did't work as the other
+users didn't have access to the drafts. The visibility check for
+the reviewer suggestion is now skipped.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1294[issue 1294]:
+  Shorten subject of parent commit for displaying in the UI
++
+If the parent commit has a very long subject (> 80 characters) shorten
+the subject for displaying it in the Gerrit web UI on the change screen.
+This avoids that the 'Parent(s)' cell for the patch set becomes very
+wide.
+
+* If subject is shortened for displaying in the UI indicate this by '...'
++
+If a commit has a very long subject line (> 80 characters) it is
+shortened when it is displayed in the Gerrit Web UI. Indicate to the
+user that the subject was shortened by appending '...' to the shortened
+subject.
++
+Also the subject is now cropped after a whitespace if possible.
+
+* Insert Change-Id for revert commits
++
+The 'Revert Change' action on a merged change allows to create a new
+change that reverts the merged change. The commit message of the revert
+commit now contains a Change-Id.
++
+It is convenient if a Change-Id is automatically created and inserted
+into the commit message of the revert commit since it makes rebasing of
+the revert commit easier.
+
+* Use more gentle shade of red to highlight outdated dependencies
+
+Patch Screens
+^^^^^^^^^^^^^
+
+* New patch screen header
++
+A new patch screen header was added that is displayed above both the
+side-by-side and unified views. The new header contains actual links to
+the available patchsets and shows which patchset is being currently
+displayed.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1192[issue 1192]:
+  Add download links to the unified diff view
+
+* Improvement of the side-by-side viewer table
++
+The line number column for the right side was moved to be on the far
+right of the table, so that the layout now looks like this:
++
+----
+  1 |  foo       |       bar   | 1
+  2 |  hello     |       hello | 2
+----
++
+This looks nicer when reading a lot of code, as the line numbers are
+less relevant than the code itself which is now in the center of the
+UI.
++
+Line numbers are still links to create comment editors, but they
+use a light shade of gray and skip the underline decoration, making
+them less visually distracting.
++
+Skip lines now use a paler shade of blue and also hide the fact they
+contain anchors, until you hover over them and the anchor shows up.
++
+The expand before and after are changed to be arrows showing in
+which direction the lines will appear above or below the skip
+line.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=626[issue 626]:
+  Option to display line endings
++
+There is a new user preference that allows to display Windows EOL/Cr-Lf.
+'\r' is shown in a dotted-line box (similar to how '\r' is displayed in
+GitWeb).
+
+* Streamlined review workflow
++
+A link was added next to the "Reviewed" checkbox that marks the current
+patch as reviewed and goes to the next unreviewed patch.
+
+* Add key commands to mark a patch as reviewed
++
+Add key commands
++
+. to toggle the reviewed flag for a patch ('m')
++
+and
++
+. to mark the patch as reviewed and navigate to the next unreviewed
+patch ('M').
+
+* Use download icons instead of the `Download` text links
+
+User Dashboard
+^^^^^^^^^^^^^^
+* Support for link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/user-custom-dashboards.html[custom
+  dashboards]
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1407[issue 1407]:
+  Improve highlighting of unreviewed changes in the user's dashboard
++
+A change will be highlighted as unreviewed if
++
+. the user is reviewer of the change but hasn't published any change
+  message for the current patch set
+. the user has published a change message for the current patch set,
+  but afterwards the change owner has published a change message on
+  the change
+
+* Sort outgoing reviews in the user dashboard by created date
+
+* Sort incoming reviews in the user dashboard by updated date
++
+Sorting the incoming reviews by last updated date, descending, places
+the most recently updated reviews at the top of the list for a user,
+and the oldest stale at the bottom. This may help users to identify
+items to take immediate action on, as they appear closer to the top.
+
+Access Rights Screen
+^^^^^^^^^^^^^^^^^^^^
+
+* Display error if modifying access rights for a ref is forbidden
++
+If a user is owner of at least one ref he is able to edit the access
+rights on a project. If he adds access rights for other refs, these
+access rights were silently ignored on save. Instead of this now an
+error message is displayed to inform the user that he doesn't have
+permissions to do the update for these refs.
++
+In case of such an error the project access screen stays in the edit
+mode so that the unsaved modifications are not lost. The user may now
+propose the changes to the access rights through code review.
+
+* Allow to propose changes to access rights through code review
++
+Users that are able to upload changes for code review for the
+`refs/meta/config` branch can now propose changes to the project access
+rights through code review directly from the ProjectAccessScreen.
++
+When editing the project access rights there is a new button
+'Save for Review' which will create a new change for the access
+rights modifications. Project owners are automatically added as
+reviewer to this change. If a project owner agrees to the access rights
+modifications he can simply approve and submit the change.
+
+* Show all access rights in WebUI if user can read `refs/meta/config`
++
+Users who can read the `refs/meta/config` branch, can see all access
+rights by fetching this branch and looking at the `project.config`
+file. Now they can see the same information in the web UI.
+
+* Allow extra group suggestions for project owners
++
+When suggesting groups to a user, only groups that are visible to the
+user are suggested. These are those group that the user is member of.
+For project owners now also groups to which they are not a member are
+suggested when editing the access rights of the project.
+
+Other
+^^^^^
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1592[issue 1592]:
+  Ask user to login if change is not found
++
+Accessing a change URL was failing with 'Application Error - The page
+you requested was not found, or you do not have permission to view this
+page' if the user was not signed in and the change was not visible to
+`Anonymous Users`. Instead Gerrit now asks the user to login and
+afterwards shows the change to the user if it exists and is visible.
+If the change doesn't exist or is not visible, the user will still get
+the NotFoundScreen after sign in.
+
+* Link to owner query from user names
++
+Instead of linking from a user name to the user's dashboards, link to
+a search for changes owned by that user.
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#gerrit.reportBugUrl[Allow
+  configuring the `Report Bug` URL]
++
+Let site administrators direct users to their own ticket queue, as for
+many servers most of the reported bugs are small internal problems like
+asking for a repository to be created or updating group memberships.
+
+* On project creation allow choosing the parent project from a popup
++
+In the create project UI a user can now browse all projects and select
+one as parent for the new project.
+
+* Check for open changes on branch deletion
++
+Check for open changes when deleting a branch in the Gerrit WebUI.
+Delete a branch only if there are no open changes for this branch.
+This makes users aware of open changes when deleting a branch.
+
+* Enable ProjectBranchesScreen for the `All-Projects` project
++
+This allows to see the branches of the `All-Projects` project in the
+web UI.
+
+* Show for each project in the project list a link to the repository
+  browser (e.g. GitWeb).
+
+* Move the project listing menu items to a new top-level item
++
+Finding the project listing was very opaque to end users. Nobody
+expected to look under `Admin` and furthermore, anonymous users were
+unable to find that link at all.
++
+Introduced a new top-level `Projects` menu that has `List` in it to
+take you to the project listing.
++
+In addition the `Create new project` link from the top of that listing
+was moved to this new menu.
+
+* Move the Groups and Plugins menu items to the top level
++
+The top-level Admin menu is removed as it is now unnecessary after the
+Projects, Groups and Plugins menu items were moved to the top-level.
+
+* Move form for group creation to own screen
++
+Move the form for the group creation from the GroupListScreen to an
+own new CreateGroupScreen and add a link to this screen at the
+beginning of the GroupListScreen. The link to the CreateGroupScreen is
+only visible if the user has the permission to create new groups.
+
+* Drop the `Owners` column from the group list screen
++
+The `Owners` column on the group list screen has been dropped in order
+to link:#performance-issue-on-showing-group-list[speed up the loading
+of the group list screen].
+
+* Drop the `Group Type` column from the group list screen
++
+Since link:#migrate-ldap-groups[the LDAP group type was removed] there
+is no need to display the group type on the group list screen anymore.
+There are only 3 `SYSTEM` groups using well known names, and everything
+else has the type `INTERNAL`.
+
+* When adding a user to a group create an account for the user if needed
++
+Trying to add a user to a group that doesn't have an account fails with
+'... is not a registered user.'. Now adding a user to a group does not
+immediately fail if there is no account for the user, but it tries to
+authenticate the user and if the authentication is successful a user
+account is automatically created, so that the user can be added to the
+group. This only works if LDAP is used as user backend.
++
+This allows to add users to groups that did not log in into Gerrit
+before.
+
+* Differentiate between draft changes and draft comments
++
+Show the draft changes of the user when he clicks on `My` > `Drafts`.
+The user's draft comments are now available under `My` >
+`Draft Comments`.
+
+* Show NotFoundScreen if a user that can't create projects tries to
+  access the ProjectCreationScreen
+
+* Add Edit, Reload next to non-editable Full Name field
++
+If the user database is actually an external system users might need go
+to another server to edit their account data, and then re-import their
+account data by going through a login cycle. This is highly similar to
+LDAP where the directory provides account data and its refreshed every
+time the user visits the `/login/` URL handler.
++
+The URL for the external system can be configured for the
+link:#custom-extension[`CUSTOM_EXTENSION`] auth type.
+
+Access Rights
+~~~~~~~~~~~~~
+
+* Restrict rebasing of a change in the web UI to the change owner and
+  the submitter
+
+* Add a new link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/access-control.html#category_rebase[
+  access right to permit rebasing changes in the web UI]
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=930[issue 930]:
+  Add new link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/access-control.html#category_abandon[
+  access right for abandoning changes]
+
+* Check if user can upload in order to restore
++
+Restoring a change is similar to uploading a new change. If a branch
+gets closed by removing the access rights to upload new changes it
+shouldn't be possible to restore changes for this branch.
+
+[[hide-config]]
+* Make read access to `refs/meta/config` by default exclusive to
+  project owners
++
+When initializing a new site a set of default access rights is
+configured on the `All-Projects` project. These default access rights
+include read access on `refs/*` for `Anonymous Users` and read access
+on `refs/meta/config` for `Project Owners`. Since the read access on
+`refs/meta/config` for `Project Owners` was not exclusive,
+`Anonymous users` were able to access the `refs/meta/config` branch
+which by default should only be accessible by the project owners.
+
+Search
+~~~~~~
+* Offer suggestions for the search operators in the search panel
++
+There are many search operators and it's difficult to remember all of
+them. Now the search operators are suggested as the user types the
+query.
+
+* Support alias `self` in queries
++
+Writing an expression like "owner:self status:open" will now identify
+changes that the caller owns and are still open. This `self` alias
+is valid in contexts where a user is expected as an argument to a
+query operator.
+
+* Add parent(s) revision information to output of query command
+
+* Add owner username to output of query command
+
+* `/query` API has been link:#query-deprecation[deprecated]
+
+SSH
+~~~
+* link:http://code.google.com/p/gerrit/issues/detail?id=1095[issue 1095]
+  link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-set-account.html[SSH command to manage
+  accounts]
+
+* On link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-create-account.html[account creation] a
+  password for HTTP can be specified.
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-set-project.html[SSH command to manage
+  project settings]
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-test-submit-rule.html[SSH command to test
+  submit rules]
++
+The command creates a fresh Prolog environment and loads a Prolog
+script from stdin. `can_submit` is then queried and the results are
+returned to the user.
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-ban-commit.html[SSH command to ban
+  commits]
+
+[[ssh-alias]]
+* Enable aliases for SSH commands
++
+Site administrators can define aliases for SSH commands in the
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#ssh-alias[`ssh-alias` section]
+of the Gerrit configuration.
+
+* Add submit records to the output of the
+  link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-query.html[query] SSH command:
++
+Add a command line option to the `query` SSH command to include submit
+records in the output.
++
+This facilitates the querying of information relating to the submit
+status from the command line and by API clients, including information
+such as whether the change can be submitted as-is, and whether the
+submission criteria for each review label has been met.
+
+* Support JSON output format for the
+  link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-ls-projects.html[ls-projects] SSH command
+
+* Support creation of multiple branches in
+  link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-create-project.html[create-project] SSH
+  command
++
+In case if a project has some kind of waterfall automerging
+a->b->c it is convenient to create all these branches at the
+project creation time.
++
+e.g. '.. gerrit create-project -b master -b foo -b bar ...'
+
+* Add verbose output option to
+  link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-ls-groups.html[ls-groups] command
++
+The verbose mode enabled by the new option makes the ls-groups
+command output a tab-separated table containing all available
+information about each group (though not its members).
+
+Documentation
+~~~~~~~~~~~~~
+
+Commands
+^^^^^^^^
+
+* document for the link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-create-group.html[`create-group`]
+  command that for unknown users an account is automatically created if
+  the LDAP authentication succeeds
+
+* Update documentation and help text for the
+  link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-review.html[`review`] SSH command
++
+The review command can be applied to multiple changes, but the
+help text was written in singular tense.
++
+Add a paragraph in the documentation explaining that the
+`--force-message` option will not be effective if the `review` command
+fails because the user is not permitted to change the label.
+
+* Clarify that `init --batch` doesn't drop old database objects
+
+* Update the list of unsupported slave commands
+
+* Fix link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-stream-events.html[`stream-events`]
+  documentation
++
+Some attributes contained in the events were not described, for a few
+others the name was given in a wrong case.
+
+* Fix and complete synopsis of commands
+
+Access Control
+^^^^^^^^^^^^^^
+
+* Clarify the ref format for
+  link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/access-control.html#category_push_merge[`Push
+  Merge Commit`]
++
+Elaborate on the required format of the ref used for `Push Merge Commit`
+access right entries to avoid user confusion when granting access to
+`refs/heads/*` still doesn't allow them to push any merge commits.
+
+* Document the
+  link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/access-control.html#capability_emailReviewers[
+  `emailReviewers`] capability
+
+Error
+^^^^^
+* Improve documentation of link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/error-change-closed.html[
+  `change closed` error]
++
+The `change closed` error can also occur when trying to submit a
+review label with the SSH review command onto a change that has
+been closed (submitted and merged, or abandoned) or onto a patchset
+that has been replaced by a newer patchset.
+
+* Correct documentation of `invalid author` and `invalid committer`
+  errors
++
+The error messages `you are not committer ...` and `you are not
+author ...` were replaced with `invalid author` and `invalid
+committer`.
+
+* Describe that the `prohibited by Gerrit` error is returned if pushing
+  a tag fails because the tagger is somebody else and the `Forge
+  Committer` access right is not assigned.
+
+Dev
+^^^
+
+* Update push URL in link:../SUBMITTING_PATCHES[SUBMITTING_PATCHES]
++
+Pushes are now accepted at the same address as clone/fetch/pull.
+
+* Update link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-contributing.html[contributor
+  document]
++
+We now prefer to use Guava (previously known as Google Collections).
+
+* Fixed broken link to source code
++
+Updated the documentation source code links to point to:
+http://code.google.com/p/gerrit/source/checkout
+
+* State link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-eclipse.html#known-problems[known issues]
+  when debugging Gerrit with Eclipse
+
+* Improved the section on
+  link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-eclipse.html#hosted-mode[hosted mode
+  debugging]
++
+The existing section on hosted mode debugging left out a couple of
+steps, and the requirement to use `DEVELOPMENT_BECOME_ANY_ACCOUNT`
+instead of `OpenID` was not mentioned anywhere.
+
+* Add a link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-release.html[release preparation
+  document]
++
+Document what it takes to make a Gerrit stable or stable-fix release,
+and how to release Gerrit subprojects.
+
+Other
+^^^^^
+* Add link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/prolog-cookbook.html[Cookbook for Prolog
+  submit rules]
++
+A new document providing a step by step introduction into implementing
+specific submit policies using Prolog based submit rules was added.
+
+* Describe link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/refs-notes-review.html[
+  `refs/notes/review` and its contents]
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-mail.html[Document `RebasedPatchSet.vm`
+  and `Reverted.vm` mail templates]
+
+* Specify output file for curl commands in documentation
++
+For downloading the `commit-msg` hook and the `gerrit-cherry-pick`
+script users can either use scp or curl. Specify the output file for
+each curl command so that the result is equal to the matching scp
+command.
+
+* Document that user must be in repository root to install `commit-msg`
+  hook
+
+* Add some clarifications to the
+  link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/install-quick.html[quick installation guide]
+
+* Add missing documentation about
+  link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#hooks[hook configuration]
++
+Add documentation of hook config for `change-restored`, `ref-updated`
+and `cla-signed` hooks.
+
+* Document that the commit message hook file should be executable
+
+* Mention that also MySQL supports replication, not just Postgres
+
+* Make sorting of release notes consistent so that the release notes
+  for the newest release is always on top
+
+* Various corrections
++
+Correct typos, spelling mistakes, and grammatical errors.
+
+Dev
+~~~
+* Add link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/dev-release.html#plugin-api[script for
+  releasing plugin API jars]
+
+* Pushes are now accepted at the same address as clone/fetch/pull
++
+To submit patches commits can be pushed to
+https://gerrit.googlesource.com/gerrit
+
+* Add `-Pchrome`, `-Pwebkit`, `-Pfirefox` aliases for building
++
+This makes it easier to build for the browser you want to
+test on, rather than remembering what its GWT name is.
+
+* Disable assertions for KeyCommandSet when running in gwtdebug mode
++
+The assertions in the KeyCommandSet class cause exceptions when a
+KeyCommand is registered several times.
+
+* Add the run profiles to the favorites menu
+
+* Add Intellij IDEA files to ignore list
+
+* Move local Maven repository to Google Cloud Storage
+
+* Make sure asciidoc uses unix line endings in generated HTML.
++
+Use an explicit asciidoc attribute to make sure the produced HTML will
+always contain unix line endings.  This will help in producing build
+results that are better comparable by size.
+
+* Remove timestamp from all `org.eclipse.core.resources.prefs` files
++
+Eclipse overwrites these files when we import projects using m2e.
+Eclipse 3 writes a timestamp at the top of these files making the Git
+working tree dirty.  Eclipse 4 (Juno) still overwrites these files but
+doesn't write the timestamp.  This should help to keep the working tree
+clean.  However, since the timestamp is currently present in these
+files, Eclispe 4 would still make them dirty by overwriting and
+effectively removing the timestamp.
++
+This change removes the timestamp from these files. This helps those
+using Eclipse 4 and doesn't make it worse for those still using Eclispe
+3.
+
+* Add Maven profile to skip build of plugin modules
++
+Building the plugin modules ('Plugin API' and 'Plugin Archetype') may
+take a significant amount of time (since many jars are downloaded).
+During development it is not needed to build the plugin modules. A new
+Maven profile was added that skips the build of the plugin modules,
+so that developers have a faster turnaround. This profile is called
+`no-plugins` and it's active by default. To include the plugin modules
+into the build activate the `all` profile:
++
+----
+  mvn clean package -P all
+----
++
+The script to make release builds has been adapted to activate the
+`all` profile so that the plugin modules are always built for release
+builds.
+
+Mail
+~~~~
+
+* Add unified diff to newchange mail template
++
+Add `$email.UnifiedDiff` as new macro to the `NewChange.vm` mail
+template. This macro is expanded to a unified diff of the patch.
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#sendemail.includeDiff[
+  sendemail.includeDiff]: Enable `$email.UnifiedDiff` in `NewChange.vm`
++
+Instead of making site administrators hack the email template, allow
+admins to enable the diff feature by setting a configuration variable
+in `gerrit.config`.
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#sendemail.maximumDiffSize[
+  sendemail.maximumDiffSize]: Limit the size of diffs sent by email
++
+If a unified diff included in an email will exceed the limit configured
+by the system administrator, only the affected file paths are listed in
+the email instead. This gives interested parties some context on the
+size and scope of the change, without killing their inbox.
+
+* Catch all exceptions when emailing change update
+
+* Allow unique from address generation
++
+Allow the from email address to be a ParameterizedString that handles
+the `${userHash}` variable. The value of the variable is the md5 hash
+of the user name. This allows unique generation of email addresses, so
+GMAIL threads names of users in conversations correctly. For example,
+the from pattern for gerrit-review defined in the Gerrit configuration
+looks like this:
++
+----
+  [sendemail]
+    from = ${user} <noreply-gerritcodereview+${userHash}@google.com>
+----
+
+* Show new change URLs in the body of the new change email
++
+Some email clients hide the signature section of an email
+automatically.  If there are no reviewers listed on a new change,
+such as when a change is pushed over HTTP and a notification is
+automatically sent out to any subscribed watchers, the URL was
+hidden inside of the signature and not readily available.
++
+Show the URL right away in the body.
+
+Miscellaneous
+~~~~~~~~~~~~~
+* Back in-memory caches with Guava, disk caches with H2
++
+Instead of using Ehcache for in-memory caches, use Guava. The Guava
+cache code has been more completely tested by Google in high load
+production environments, and it tends to have fewer bugs. It enables
+caches to be built at any time, rather than only at server startup.
++
+By creating a Guava cache as soon as it is declared, rather than
+during the LifecycleListener.start() for the CachePool, we can promise
+any downstream consumer of the cache that the cache is ready to
+execute requests the moment it is supplied by Guice. This fixes a
+startup ordering problem in the GroupCache and the ProjectCache, where
+code wants to use one of these caches during startup to resolve a
+group or project by name.
++
+Tracking the Gauva backend caches with a DynamicMap makes it possible
+for plugins to define their own in-memory caches using CacheModule's
+cache() function to declare the cache. It allows the core server to
+make the cache available to administrators over SSH with the gerrit
+show-caches and gerrit `flush-caches` commands.
++
+Persistent caches store in a private H2 database per cache, with a
+simple one-table schema that stores each entry in a table row as a
+pair of serialized objects (key and value). Database reads are gated
+by a BloomFilter, to reduce the number of calls made to H2 during
+cache misses. In theory less than 3% of cache misses will reach H2 and
+find nothing. Stores happen on a background thread quickly after the
+put is made to the cache, reducing the risk that a diff or web_session
+record is lost during an ungraceful shutdown.
++
+Cache databases are capped around 128M worth of stored data by running
+a prune cycle each day at 1 AM local server time. Records are removed
+from the database by ordering on the last access time, where last
+accessed is the last time the record was moved from disk to memory.
+
+* Add OpenID SSO support.
++
+Setting `OPENID_SSO` for
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#auth.type[`auth.type`] in the
+`gerrit.config` will allow the admin to specify an SSO entry point URL
+so that users clicking on "Sign In" are sent directly to that URL.
+
+* Git over HTTP BasicAuth against Gerrit basic auth.
++
+Allows the configuration of native Gerrit username/password
+authentication scheme used for Git over HTTP BasicAuth, as alternative
+of the default DigestAuth scheme against the random generated password
+on Gerrit DB.
++
+Example setting for link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#auth.type[
+`auth.type`] and link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#auth.gitBasicAuth[
+`auth.gitBasicAuth`]:
++
+----
+  [auth]
+    type = LDAP
+    gitBasicAuth = true
+----
++
+With this configuration Git over HTTP protocol will be authenticated
+using `HTTP-BasicAuth` and credentials checked on LDAP.
+
+* Abstract group systems into GroupBackend interface
++
+Group backends are supposed to use unique prefixes to isolate the
+namespaces. E.g. the group backend for LDAP is using `ldap/` as prefix
+for the group names.
++
+This means that to refer to an LDAP group in the WebUI the group name
+needs to be prefixed with the `ldap/` string. E.g. if there is a group
+in LDAP which is called "Developers", Gerrit will suggest this group
+when the user types `ldap/De`.
++
+WARNING: External groups are not anymore allowed to be members of
+internal groups.
+
+[[migrate-ldap-groups]]
+* Migrate existing internal LDAP groups
++
+Previously, LDAP groups were mirrored in the AccountGroup table and
+given an Id and UUID the same as internal groups. Update these groups
+to be backed by only a GroupReference, with a special "ldap:" UUID
+prefix. Migrate all existing references to the UUID in ownerGroupUUID
+and any `project.config`.
++
+This made the LDAP group type obsolete and it was removed.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=548[issue 548]:
+  Make commands to download patch sets
+  link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#download.command[configurable]
++
+For patch sets on the ChangeScreen different commands for downloading
+the patch sets are offered. For some installations not all commands are
+needed. Allow Gerrit administrators to configure which download
+commands should be offered.
+
+* Add more link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#theme[theme color
+  options]
++
+** Add a theme option to change outdated background color
+** Add odd/even row background color for tables such as list of open
+reviews.  This makes them more visible without clicking on them.
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/user-notify.html[Add `notify` section in
+  `project.config`]
++
+The notify section allows project owners to include emails to users
+directly from `project.config`. This removes the need to create fake
+user accounts to always BCC a group mailing list.
+
+* Include the contributor agreements in the `project.config` and
+  migrate contributor agreements to `All-Projects`
++
+Update the parsing of `project.config` to support the contributor
+agreements.
++
+Add a new schema to move the ContributorAgreement, AccountAgreement,
+and AccountGroupAgreement information into the `All-Projects`
+`project.config`.
+
+* Add `sameGroupVisibility` to `All-Projects` `project.config`
++
+The `sameGroupVisiblity` is needed to restrict the visibility of
+accounts when `accountVisibility` is `SAME_GROUP`. Namely, this is a
+way to make sure the `autoVerify` group in a `contributor-agreements`
+section is never suggested.
+
+* Add change topic in hook arguments
++
+It was not possible for hook scripts to include topic-specific
+behaviour because the topic name was not included in the arguments.
+
+* Add `--is-draft` argument on `patchset-created` hook
++
+The `--is-draft` argument will be passed with either `true` if
+the patchset is a draft, or `false` otherwise.
++
+This can be used by hooks that need to behave differently if the
+change is a draft.
+
+* Log sign in failures on info level
++
+If for a user signing in into the Gerrit web UI fails, this can have
+many reasons, e.g. username is wrong, password is wrong, user is marked
+as inactive, user is locked in the user backend etc. In all cases the
+user just gets a generic error message 'Incorrect username or
+password.'. Gerrit administrators had trouble to find the exact reason
+for the sign in problem because the corresponding AccountException was
+not logged.
+
+* Do not log 'Object too large' as error with full stacktrace
++
+If a user pushes an object which is larger than the configured
+`receive.maxObjectSizeLimit` parameter, the push is rejected with an
+'Object too large' error. In addition an error log entry with the full
+stacktrace was written into the error log.
++
+This is not really a server error, but just a user doing something that
+is not allowed, and thus it should not be logged as error. For a Gerrit
+administrator it might still be interesting how often the limit is hit.
+This is why it makes sense to still log this on info level.
++
+For the user pushing a too large object we now do not print the
+'fatal: Unpack error, check server log' message anymore, but only the
+'Object too large' error message.
+
+* Add better explanations to rejection messages
++
+Provide information to the user why a certain push was rejected.
+
+* Automatic schema upgrade on Gerrit startup
++
+In case when Gerrit administrator(s) don't have a direct access to the
+file system where the review site is located it gets difficult to
+perform a schema upgrade (run the init program). For such cases it is
+convenient if Gerrit performs schema upgrade automatically on its
+startup.
++
+Since this is a potentially dangerous operation, by default it will not
+be performed. The configuration parameter
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#site.upgradeSchemaOnStartup[
+site.upgradeSchemaOnStartup] is used to switch on automatic schema
+upgrade.
+
+* Shorten column names that are longer than 30 characters
++
+Some databases can't deal with column names that are longer than 30
+characters. Examples are MaxDB and
+link:http://groups.google.com/group/repo-discuss/browse_thread/thread/ecb713d42c04ae8a/cc963525d8247a17?lnk=gst#cc963525d8247a17[Oracle].
++
+Gerrit had two column names in the `accounts` table that exceeded the
+30 characters: `displayPatchSetsInReverseOrder`,
+`displayPersonNameInReviewCategory`
++
+These 2 columns were renamed so that their names fit within the 30
+character range.
+
+* Increase the maximum length for tracking ID's to 32 characters
++
+So far tracking ID's had a maximum length of only 20 characters.
+
+* Set `GERRIT_SITE` in Gerrit hooks as environment variable
++
+Allows development of hooks parametrised on Gerrit location. This can
+be useful to allow hooks to load the Gerrit configuration when needed
+(from `$GERRIT_SITE`) or even store their additional config files under
+`$GERRIT_SITE/etc` and retrieve them at startup.
+
+* Add an exponentially rolling garbage collection script
++
+`git-exproll.sh` is a git garbage collection script aimed specifically
+at reducing exccessive garbage collection and particularly large
+packfile churn for Gerrit installations.
++
+Excessive garbage collection on "dormant" repos is wasteful of both CPU
+and disk IO.  Large packfile churn can lead to heavy RAM and FS usage
+on Gerrit servers when the Gerrit process continues to hold open the
+old delete packfiles.  This situation is most detrimental when jgit is
+configured with large caching parameters.  Aside from these downsides,
+running git gc often can be very beneficial to performance on servers.
+This script attempts to implement a git gc policy which avoids the
+downsides mentioned above so that git gc can be comfortably run very
+regularly.
++
+`git-exproll.sh` uses keep files to manage which files will get
+repacked.  It also uses timestamps on the repos to detect dormant repos
+to avoid repacking them at all.  The primary packfile objective is to
+keep around a series of packfiles with sizes spaced out exponentially
+from each other, and to roll smaller packfiles into larger ones once
+the smaller ones have grown.  This strategy attempts to balance disk
+space usage with avoiding rewriting large packfiles most of the time.
++
+The exponential packing objective above does not save a large amount of
+time or CPU, but it does prevent the packfile churn.  Depending on repo
+usage, however the dormant repo detection and avoidance can result in a
+very large time savings.
+
+* Automatically flush persistent H2 cache if the existing cache entries
+  are incompatible with the cache entry class and thus can't be
+  deserialized
+
+* Unpack JARs for running servers in `$site_path/tmp`
++
+Instead of unpacking a running server into `~/.gerritcodereview/tmp`
+only use that location for commands like init where there is no active
+site. From gerrit.sh always use `$site_path/tmp` for the JARs to
+isolate servers that run on the same host under the same UNIX user
+account.
+
+[[custom-extension]]
+* Allow for the `CUSTOM_EXTENSION` `auth.type` to configure URLs for
+  editing the user name and obtaining an HTTP password
++
+Allow `CUSTOM_EXTENSION` auth type to supply by `auth.editFullNameUrl`
+a URL in the web UI that links users to the other account system,
+where they can edit their name, and then use another reload URL to
+cycle through the `/login/` step and refresh the data cached by Gerrit.
++
+Allow `CUSTOM_EXTENSION` auth type to supply by `auth.httpPasswordUrl`
+a URL in the web UI that allows users to obtain an HTTP password.
++
+Like the rest of the `CUSTOM_EXTENSION` stuff, this is hack that will
+eventually go away when there is proper support for authentication
+plugins.
+
+Performance
+~~~~~~~~~~~
+[[performance-issue-on-showing-group-list]]
+* Fix performance issues on showing the list of groups in the Gerrit
+  WebUI
++
+Loading `Admin` > `Groups` on large servers was very slow. The entire
+group membership database was downloaded to the browser when showing
+just the list of groups.
++
+Now the amount of data that needs to be downloaded to the browser is
+reduced by using the more leightweight `AccountGroup` type instead of
+the `GroupDetail` type when showing the groups in a list format. As a
+consequence the `Owners` column that showed the name of the owner group
+had been dropped.
+
+* Add LDAP-cache to minimize number of queries when unnesting groups
++
+A new cache named "ldap_groups_byinclude" is introduced to help lessen
+the number of queries needed to resolve nested LDAP-groups.
+
+* Add index for accessing change messages by patch set
++
+This improves the performance of loading the dashboards.
+
+* Add a fast path to avoid checking every commit on push
++
+If a user can forge author, committer and gerrit server identity, and
+can upload merges, don't bother checking the commit history of what is
+being uploaded. This can save time on servers that are trying to accept
+a large project import using the push permission.
+
+* Improve performance of `ReceiveCommits` by reducing `RevWalk` load
++
+JGit RevWalk does not perform well when a large number of objects are
+added to the start set by `markStart` or `markUninteresting`. Avoid
+putting existing `refs/changes/` or `refs/tags/` into the `RevWalk` and
+instead use only the `refs/heads` namespace and the name of the branch
+used in the `refs/for/` push line.
++
+Catch existing changes by looking for their exact commit SHA-1, rather
+than complete ancestory. This should have roughly the same outcome for
+anyone pushing a new commit on top of an existing open change, but
+with lower computional cost at the server.
+
+* Lookup changes in parallel during `ReceiveCommits`
++
+If the database has high query latency, the loop that locates existing
+changes on the destination branch given Change-Id can be slow. Start
+all of the queries as commits are discovered, but don't block on
+results until all queries were started.
++
+If the database can build the `ResultSet` in the background, this may
+hide some of the query latency by allowing the queries to overlap when
+more than one lookup must be performed for a push.
+
+* Perform change update on multiple threads
++
+When multiple changes need to be created or updated for a single push
+operation they are now inserted into the database by parallel threads,
+up to the maximum allowed thread count. The current thread is used
+when the thread pool is already fully in use, falling back to the
+prior behavior where each concurrent push operation can do its own
+concurrent database update. The thread pool exists to reduce latency
+so long as there are sufficient threads available.
++
+This helps push times on databases that are high latency, such as
+database servers that are running on a different machine from the
+Gerrit server itself, e.g. gerrit.googlesource.com.
++
+The new thread pool is
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#receive.changeUpdateThreads[
+disabled by default], limiting the overhead to servers that have good
+latency with their database, such as using in-process H2 database, or
+a MySQL or PostgreSQL on the same host.
+
+* Use `BatchRefUpdate` to execute reference changes
++
+Some storage backends for JGit are able to update multiple references
+in a single pass efficiently. Take advantage of this by pushing
+any normal reference updates (such as direct push or branch create)
+into a single `BatchRefUpdate` object.
+
+* Assume labels are correct in ListChanges
++
+To reduce end-user latency when displaying changes in a search result
+or user dashboard, assume the labels are accurate in the database at
+display time and don't recompute the access privileges of a reviewer.
+
+* Notify the cache that the git_tags was modified
++
+The tag cache was updated in-place, which prevented the H2 based
+storage from writing out the updated tag information. This meant
+servers almost never had the right data stored on disk and had to
+recompute it at startup.
++
+Anytime the value is now modified in place, put it back into the
+cache so it can be saved for use on the next startup.
+
+* Special case hiding `refs/meta/config` from Git clients
++
+VisibleRefFilter requires a lot of server CPU to accurately provide
+the correct listing to clients when they cannot read `refs/*`.
++
+Since the default configuration is now to link:#hide-config[
+hide `refs/meta/config`], use a special case in VisibleRefFilter that
+permits showing every reference except `refs/meta/config` if a user can
+read every other reference in the repository.
+
+* Avoid second remote call to lookup approvals when loading change
+  results
++
+By using the new link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/rest-api.html#changes[`/changes/`]
+REST endpoint the web UI client now obtains the label information
+during the query and avoids a second round trip to lookup the current
+approvals for each displayed change. For most users this should improve
+the way the page renders. The verified and code review columns will be
+populated before the table is made visible, preventing the layout from
+"jumping" the way the old UI did when the 2nd RPC finally finished and
+supplied the label data.
+
+* Load patch set approvals in parallel
++
+ResultSet is a future-like interface, the database system is free to
+execute each result set asynchronously in the background if it
+supports that. gwtorm's default SQL backend always runs queries
+immediately and then returns a ListResultSet, so for most installs this
+has no real impact in ordering.
++
+For the system that runs gerrit-review, each query has a high cost in
+network latency, the system treats ResultSet as a future promise to
+supply the matching rows. Getting all of the necessary ResultSets up
+front allows the database to send all requests to the backend as early
+as possible, allowing the network latency to overlap.
+
+Upgrades
+--------
+* Update Gson to 2.1
+* Update GWT to 2.4.0
+* Update JGit to 2.0.0.201206130900-r.23-gb3dbf19
+
+* Use gwtexpui 1.2.6
++
+** Hide superfluous status text from clippy flash widget
+** Fix diappearance of text in CopyableLabel when clicking on it
+
+* Update Guava to 12.0.1
++
+This fixes a performance problem with LoadingCache where the cache's
+inner table did not dynamically resize to handle a larger number
+of cached items, causing O(N) lookup performance for most objects.
+
+Bug Fixes
+---------
+
+Security
+~~~~~~~~
+* Ensure that only administrators can change the global capabilities
++
+Only Gerrit server administrators (members of the groups that have
+the `administrateServer` capability) should be able to edit the
+global capabilities because being able to edit the global capabilities
+means being able to assign the `administrateServer` capability.
++
+Because of this on the `All-Projects` project it is disallowed to assign
++
+. the `owner` access rights on `refs/*`
++
+Project owners (members of groups to which the `owner` access right
+is assigned) are able to edit the access control list of the projects
+they own. Hence being owner of the `All-Projects` project would allow
+to edit the global capabilities and assign the `administrateServer`
+capabilitiy without being Gerrit administrator.
++
+In earlier Gerrit versions (2.1.x) it was already implemented like
+this but the corresponding checks got lost.
++
+. the 'push' access right on `refs/meta/config`
++
+Being able to push configuration changes to the `All-Projects` project
+allows to edit the global capabilities and hence a user with this
+access right could assign the `administrateServer` capability without
+being Gerrit administrator.
++
+From the Gerrit WebUI (ProjectAccessScreen) it is not possible anymore
+to assign on the `All-Projects` project the `owner` access right on
+`refs/*` and the `push` access right on `refs/meta/config`.
++
+In addition it is ensured that an `owner` access right that is assigned
+for `refs/*` on the `All-Projects` project has no effect and that only
+Gerrit administrators with the `push` access right can push
+configuration changes to the `All-Projects` project.
++
+It is still possible to assign both access rights (`owner` on `refs/*`
+and `push` on `refs/meta/config`) on the `All-Projects` project by directly
+editing its `project.config` file and pushing to `refs/meta/config`.
+To fix this it would be needed to reject assigning these access rights
+on the `All-Projects` project as invalid configuration, however doing this
+would mean to break existing configurations of the `All-Projects` project
+that assign these access rights. At the moment there is no migration
+framework in place that would allow to migrate `project.config` files.
+Hence this check is currently not done and these access rights in this
+case have simply no effect.
+
+Web
+~~~
+
+* Do not show "Session cookie not available" on sign in
++
+When LDAP is used for authentication, clicking on the 'Sign In' link
+opens a user/password dialog. In this dialog the "Session cookie not
+available." message was always shown as warning. This warning was
+pretty useless since the user was about to sign in because he had no
+current session.
++
+This problem was discussed on the
+link:https://groups.google.com/forum/#!topic/repo-discuss/j-t77m8-7I0/discussion[
+Gerrit mailing list].
+
+* Reject restoring a change if its destination branch does not exist
+  anymore
+
+* Reject submitting a change if its destination branch does not exist
+  anymore
++
+If a branch got deleted and there was an open change for this branch,
+it was still possible to submit this open change. As result the
+destination branch was implicitly recreated, even if the user
+submitting the change had no privileges to create branches.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1352[issue 1352]:
+  Don't display "Download" link for `/COMMIT_MSG`
++
+The commit message file is special, it doesn't actually exist and
+cannot be downloaded. Don't offer the download link in the side by
+side viewer.
+
+* Dependencies were lost in the ChangeScreen's "Needed By" table
++
+Older patchsets are now iterated for decendents, so that the dependency
+chain does not break on new upstream patchsets.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1442[issue 1442]:
+  Only show draft change dependency if current user is owner or reviewer
++
+In the change screen, the dependencies panel was showing draft changes
+in the "Depends On" and "Needed By" lists for all users, and when there
+was no user logged in.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1558[issue 1558]:
+  Create a draft patch set when a draft patch set is rebased
++
+Rebasing a draft patch set created a non-draft patch set. It was
+unexpected that rebasing a draft patch set published the modifications
+done in the draft patch set.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1176[issue 1176]:
+  Fix disappearance of download command in Firefox
++
+Clicking on the download command for a patch set in Firefox made the
+download command disappear.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1587[issue 1587]:
+  Fix disappearance of action buttons when selecting the last patch set
+  as `Old Version History`
+
+* Fix updating patch list when `Old Version History` is changed
++
+If a collapsed patch set panel was expanded and re-closed it's patch
+list wasn't updated anymore when the selection for `Old Version History`
+was changed.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1523[issue 1523]:
+  Update diff base to match old version history
++
+When changing the diff base in the `Old Version History` on the change
+screen and then entering the Side-By-Side view for a file, clicking on
+the back button in the browser (reentering the change screen) was
+causing the files to be wrongly compared with `Base` again.
+
+* Don't NPE if current patch set is not available
++
+Broken changes may have the current patch set field incorrectly
+specified, causing currentPatchSet to be unable to locate the
+correct data and return it. When this happens don't NPE, just
+claim the change is not reviewed.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=1555[issue 1555]:
+  Fix displaying of file diff if draft patch has been deleted
++
+Displaying any file diff for a patch set failed if the change had any
+gaps in its patch set history. Patch sets can be missing, if they
+have been drafts and were deleted.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=856[issue 856]:
+  Fix displaying of comments on deleted files
++
+Published and draft comments that are posted on deleted files were not
+loaded and displayed.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=735[issue 735]:
+  Fix `ArrayIndexOutOfBoundsException` on navigation to next/previous
+  patch
++
+An `ArrayIndexOutOfBoundsException` could occur when navigating from
+one patch to the next/previous patch if the next/previous patch was a
+newly added binary file. The exception occurred if the user was not
+signed in or if the user was signed in and had `Syntax Coloring` in the
+preferences enabled.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=816[issue 816]:
+  Fix wrong file indention in Side-by-Sie diff viewer on right side
+
+* Only set reviewed attribute on open changes
++
+If a change is merged or abandoned, do not consider the reviewed
+property for the calling user, so that the change is not highlighted
+as unreviewed on the user's dashboard.
+
+* Change PatchTable pointer when loading patch
++
+This patch fixes an issue with the "file list" table displayed by
+clicking on the "Files" sub-menu when viewing a diff.
++
+Originally when navigating between patch screens the highlighted row
+(pointer) of the file list table would not change when not directly
+interacting with the table e.g. by clicking on the previous or next
+file link.
++
+This patch updates the file list table whenever a new patch screen is loaded
+so that the pointer corresponds to the current patch being displayed.
+
+* Don't hyperlink non-internal groups
++
+When an external group (such as LDAP) is used in a permission rule,
+don't attempt to link to the group in the internal account system UI.
+The group won't load successfully. Instead just display the name and
+put the UUID into a tooltip to show the full DN.
+
+* Fix: Popup jumps back to original position when resizing screen
++
+On 'Watched Projects' screen, the 'Browse' button displays a popup
+window. If the user moves it and then resizes the screen, it won't snap
+back to the original position.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1457[issue 1457]:
+  Prevent groups from being renamed to empty string
+
+* Fixed AccountGroupInfoScreen search callback
++
+If the search returned no results, the search button would not be
+enabled and the status panel was not shown. Fixed the panel and button
+to always be enabled.
+
+* Fix NullPointerException on `/p/`
++
+Requesting just `/p/` caused a NullPointerException as the redirection
+logic had no project name to form a URL from. Detect requests for `/p/`
+and redirect to 'Admin' > 'Projects' to show the projects the caller
+has access to.
+
+Mail
+~~~~
+
+* Fix: Rebase did not mail all reviewers
+
+* Fix email showing in AccountLink instead of names
++
+Prefer the full name for the display text of the link.
+
+* Fix signature delimiter for e-mail messages
++
+Make sure the signature delimiter is "-- " (two dashes and a space).
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1397[issue 1397]:
+  Don't wait for banner message from SMTP server after STARTTLS
+  negotiation
++
+According to RFC 2847 section 5.2, SMTP server won't send the banner
+message again after STARTTLS negotiation. The original code will hang
+until SMTP server kicks it off due to timeout and can't send email with
+STARTTLS enabled, aka. `sendemail.smtpEncryption = tls`.
+
+* Extract all mail templates during site init
++
+The example mail templates `RebasedPatchSet.vm`, `Restored.vm` and
+`Reverted.vm` were not extracted during the initialization of a new
+site.
+
+SSH
+~~~
+* Fix reject message if bypassing code review is not allowed
++
+If a user is not allowed to bypass code review, but tries to push a
+commit directly, Gerrit rejected this push with the error message
+"can not update the reference as a fast forward". This message was
+confusing to the user since the push only failed due to missing
+access rights. Go back to the old message that says "prohibited
+by Gerrit".
+
+* Fix reject message if pushing tag is rejected because tagger is
+  somebody else
++
+Pushing a tag that has somebody else as tagger requires the `Forge
+Committer` access right. If this access right was missing Gerrit
+was rejecting the push with "can not create new references". This error
+message was misleading because the user may have thought that the
+`Create Reference` access right was missing which was actually assigned.
++
+The same reject message was also returned on push of an annotated tag
+if the `Push Annotated Tag` access right was missing. Also in this case
+the error message was not ideal.
++
+Go back to the old more generic message which says `prohibited by
+Gerrit`.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1437[issue 1437]:
+  Send event to stream when draft change is published
++
+When a change is uploaded as a draft, a `patchset-created` event is
+sent to the event stream, but since drafts are private to the owner,
+the event is not publicly visible.  When the draft is later published,
+no publicly visible event was sent. As result of this external tools
+that rely on the event stream to detect new changes didn't receive
+events for any changes that were first uploaded as draft.
++
+There is now a new event, `draft-published`, which is sent to the
+event stream when a draft change is published.  The content of this
+event is the same as `patchset-created`.
+
+* Fix: Wrong ps/rev in `change-merged` stream-event
++
+When using cherry-pick as merge strategy, the wrong ref was set in the
+`change-merged` stream-event.
++
+The issue stems from Gerrit would not acknowledge the resulting new
+pachset (the actual cherry-pick).
+
+* Fix NullPointerException in `query` SSH command
++
+Running the `query` SSH command with the options `--comments` and
+`--format=JSON` failed with a NullPointerException if a change had a
+message without author. Change messages have no author if they were
+created by Gerrit. For such messages now the Gerrit Server identity is
+returned as author.
+
+* Fix the `export-review-notes` command's Guice bindings
++
+The `export-review-notes` command was broken becasue of the CachePool
+class being bound twice. The startup of the command failed because of
+that.
+
+* Fix sorting of SSH help text
++
+Commands were displaying in random order, sort commands before output.
+
+* `replicate` command: Do not log errors for wrong user input
++
+If the user provided an invalid combination of command options or an
+non existing project name this was logged in the `error.log` but
+printing the error out to the user is sufficient.
+
+Authentication
+~~~~~~~~~~~~~~
+
+* Fix NPE in LdapRealm caused by non-LDAP users
++
+Servers that are connected to LDAP but have non-LDAP user accounts
+created by `gerrit create-account` (e.g. batch role accounts for
+build systems) were crashing with a NullPointerException when the
+LdapRealm tried to discover which LDAP groups the non-LDAP user
+was a member of in the directory.
+
+* Fix domain field of HTTP digest authentication
++
+Per RFC 2617 the domain field is optional. If it is not present,
+the digest token is valid on any URL on the server. When set it
+must be a path prefix describing the URLs that the password would
+be valid against.
++
+When a canonical URL is known, supply that as the only domain that
+is valid. When the URL is missing (e.g. because the provider is
+still broken) rely on the context path of the application instead.
+
+Replication
+~~~~~~~~~~~
+
+* Fix inconsistent behaviour when replicating `refs/meta/config`
++
+In `replication.config`, if `authGroup` is set to be used together with
+`mirror = true`, refs blocked through the `authGroup` are deleted from
+the slave/mirror. The same correctly applies if the `authGroup` is used
+to block `refs/meta/config`.
++
+However, if `replicatePermission` was set to `false`, Gerrit was
+refusing to clean up `refs/meta/config` on the slave/mirror.
+
+* Fix bug with member assignment order in PushReplication.
++
+The groupCache was being used before it was set in the class. Fix the
+ordering of the assignment.
+
+Approval Categories
+~~~~~~~~~~~~~~~~~~~
+
+* Make `NoBlock` and `NoOp` approval category functions work
++
+The approval category functions `NoBlock` and `NoOp` have not worked
+since the integration of Prolog.
++
+`MAY` was introduced as a new submit record status to complement `OK`,
+`REJECT`, `NEED`, and `IMPOSSIBLE`. This allows the expression of
+approval categories (labels) that are optional, i.e. could either be
+set or unset without ever influencing whether the change could be
+submitted. Previously there was no way to express this property in
+the submit record.
++
+This enables the `NoBlock` and `NoOp` approval category functions to
+work as they now emit may() terms from the Prolog rules. Previously
+they returned ok() terms lacking a nested user term, leading to
+exceptions in code that expected a user context if the label was `OK`.
+
+* Fix category block status without negative score
++
+Categories without blocking or approval scores will result in the
+blocking/approved image appearing in the category column after changes
+are merged should the score by the reviewer match the minimum or
+maximum value respectively.
++
+A check to ignore "No Score" values of 0 was added.
+
+* Don't remove dashes from approval category name
++
+If an approval category name contained a dash, it was removed by
+Gerrit. On the other side a space in an approval category name is
+converted to a dash. This was confusing for writing Prolog submit
+rules. If, for example, one defined a new category named `X-Y`, then in
+the Prolog code the proper name for that category would have been `XY`
+which was unintuitive.
+
+* Fix NPE in `PRED__load_commit_labels_1`
++
+If a change query uses reviewer information and loads the approvals
+map, but there are no approvals for a given patch set available, the
+collection came out null, which cannot be iterated. Make it always be
+an empty list.
+
+Other
+~~~~~
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1554[issue 1554]:
+  Fix cloning of new projects from slave servers
++
+If a new project is created in Gerrit the replication creates the
+repository for this new project directly in the filesystem of the slave
+server. The slave server was not discovering this new repository and as
+result any attempt to clone the corresponding project from the slave
+server failed.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1548[issue 1548]:
+  Create a ref for the patch set that is created when a change is
+  cherry-picked and trigger the replication for it:
++
+If Cherry Pick is chosen as submit type, on submit a new commit is
+created by the cherry-pick. For this commit a new patch set is created
+which is added to the change. Using any of the download commands to
+fetch this new patch set failed with 'Couldn't find remote ref' because
+no ref for the new patch set was created.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1626[issue 1626]:
+  Fix NullPointerException on cherry-pick if `changeMerge.test` is enabled
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1491[issue 1491]:
+  Fix nested submodule updates
+
+* Set link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#transfer.timeout[transfer
+  timeout] for pushes through HTTP
++
+The transfer timeout was only set when pushing via SSH.
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/config-gerrit.html#receive.maxObjectSizeLimit[
+  Limit maximum Git object size] when pushing through HTTP
++
+The limit for the maximum object size was only set when pushing via SSH.
+
+* Fix units of `httpd.maxwait`
++
+The default unit here is minutes, but Jetty wants to get milliseconds
+from the maxWait field. Convert the minutes returned by getTimeUnit to
+be milliseconds, matching what Jetty expects.
++
+This should resolve a large number of 503 errors for Git over HTTP.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1493[issue 1493]:
+  Fix wrong "change ... closed" message on direct push
++
+Pushing a commit directly into the central repository with bypassing
+code review wrongly resulted in a "change ... closed" message if the
+commit was already pushed for review and if a Change-Id was included in
+the commit message. Despite of the error message the push succeeded and
+the corresponding change got closed. Now the message is not printed
+anymore.
+
+* Fix NPE that can hide guice CreationException on site init
++
+Note that the `--show-stack-trace` option is needed to print the stack
+trace when a program stops with a Die exception.
+
+* Do not automatically add author/committer as reviewer to drafts
+
+* Do not automatically add reviewers from footer lines to drafts
+
+* Fix NullPointerException in MergeOp
++
+The body of the commit object may have been discarded earlier to
+save memory, so ensure it exists before asking for the author.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1396[issue 1396]:
+  Initialize the submodule commit message buffer
+
+* Fix file name matching in `commit_delta` to perform substring
+  matching
++
+The `commit_delta` predicate was matching the entire file name against
+the given regular expression while other predicates (`commit_edits`,
+`commit_message_matches`) performed substring matching. It was
+inconsistent that for `commit_delta` it was needed to write something
+like:
++
+----
+  commit_delta('.*\.java')
+----
++
+to match all `*.java` files, while for `commit_edits` it was:
++
+----
+  commit_edits('\.java$', '...')
+----
++
+to match the same set of (Java) files.
+
+* Create index for submodule subscriptions on site upgrade
+
+* Fix URL to Jetty XML DTDs so they can be properly validated
+
+* Fix resource leak when `changeMerge.test` is `true`
+
+* Fix possible synchronization issue in TaskThunk
+
+* Fix possible NPEs in `ReplaceRequest.cmd` usage in `ReceiveCommits`
++
+The `cmd` field is populated by `validate(boolean)`. If this method
+fails, results on some `ReplaceRequests` may not be set. Guard the
+attempt to access the field with a null check.
+
+* Match no labels if current patch set is not available
++
+If the current patch set cannot be loaded from `ChangeData`, assume no
+label information. This works around an NullPointerException inside of
+`ChangeControl` where the `PatchSet` is otherwise required.
+
+* Create new patch set references before database records
++
+Ensure the commit used by a new change or replacement patch set
+always exists in the Git repository by writing the reference first
+as part of the overall `BatchRefUpdate`, then inserting the database
+records if all of the references stored successfully.
+
+* Fix rebase patch set and revert change to update Git first
++
+Update the Git reference before writing to the database. This way the
+repository cannot be corrupted if the server goes down between the two
+actions.
+
+* Make sure we use only one type of NoteMerger for review notes creation
+
+* Fix generation of owner group in GroupDetail
++
+Set the GroupDetail.ownerGroup to the AccountGroup.ownerGroupUUID
+instead of the groupUUID.
+
+* Ensure that ObjectOutputStream in H2CacheImpl is closed
+
+* Ensure that RevWalk in SubmoduleOp is released
diff --git a/ReleaseNotes/index.txt b/ReleaseNotes/index.txt
index 5f8de28..07e6aa5 100644
--- a/ReleaseNotes/index.txt
+++ b/ReleaseNotes/index.txt
@@ -1,6 +1,15 @@
 Gerrit Code Review - Release Notes
 ==================================
 
+[[2_5]]
+Version 2.5.x
+-------------
+* link:ReleaseNotes-2.5.4.html[2.5.4]
+* link:ReleaseNotes-2.5.3.html[2.5.3]
+* link:ReleaseNotes-2.5.2.html[2.5.2]
+* link:ReleaseNotes-2.5.1.html[2.5.1]
+* link:ReleaseNotes-2.5.html[2.5]
+
 [[2_4]]
 Version 2.4.x
 -------------
@@ -11,26 +20,26 @@
 [[2_3]]
 Version 2.3.x
 -------------
-* link:ReleaseNotes-2.3.html[2.3]
 * link:ReleaseNotes-2.3.1.html[2.3.1]
+* link:ReleaseNotes-2.3.html[2.3]
 
 [[2_2]]
 Version 2.2.x
 -------------
-* link:ReleaseNotes-2.2.2.html[2.2.2],
-* link:ReleaseNotes-2.2.2.2.html[2.2.2.2],
-* link:ReleaseNotes-2.2.2.1.html[2.2.2.1],
-* link:ReleaseNotes-2.2.1.html[2.2.1],
+* link:ReleaseNotes-2.2.2.2.html[2.2.2.2]
+* link:ReleaseNotes-2.2.2.1.html[2.2.2.1]
+* link:ReleaseNotes-2.2.2.html[2.2.2]
+* link:ReleaseNotes-2.2.1.html[2.2.1]
 * link:ReleaseNotes-2.2.0.html[2.2.0]
 
 [[2_1]]
 Version 2.1.x
 -------------
-* link:ReleaseNotes-2.1.8.html[2.1.8],
-* link:ReleaseNotes-2.1.7.2.html[2.1.7.2],
-* link:ReleaseNotes-2.1.7.html[2.1.7],
-* link:ReleaseNotes-2.1.6.html[2.1.6],
-  link:ReleaseNotes-2.1.6.1.html[2.1.6.1]
+* link:ReleaseNotes-2.1.8.html[2.1.8]
+* link:ReleaseNotes-2.1.7.2.html[2.1.7.2]
+* link:ReleaseNotes-2.1.7.html[2.1.7]
+* link:ReleaseNotes-2.1.6.1.html[2.1.6.1]
+* link:ReleaseNotes-2.1.6.html[2.1.6]
 * link:ReleaseNotes-2.1.5.html[2.1.5]
 * link:ReleaseNotes-2.1.4.html[2.1.4]
 * link:ReleaseNotes-2.1.3.html[2.1.3]
@@ -40,31 +49,31 @@
 * link:ReleaseNotes-2.1.2.2.html[2.1.2.2]
 * link:ReleaseNotes-2.1.2.1.html[2.1.2.1]
 * link:ReleaseNotes-2.1.2.html[2.1.2]
-* link:ReleaseNotes-2.1.1.html[2.1.1],
-  link:ReleaseNotes-2.1.1.html[2.1.1.1]
+* link:ReleaseNotes-2.1.1.html[2.1.1.1]
+* link:ReleaseNotes-2.1.1.html[2.1.1]
 * link:ReleaseNotes-2.1.html[2.1]
 
 [[2_0]]
 Version 2.0.x
 -------------
-* link:ReleaseNotes-2.0.24.html[2.0.24],
-  link:ReleaseNotes-2.0.24.html[2.0.24.1],
-  link:ReleaseNotes-2.0.24.html[2.0.24.2]
+* link:ReleaseNotes-2.0.24.html[2.0.24.2]
+* link:ReleaseNotes-2.0.24.html[2.0.24.1]
+* link:ReleaseNotes-2.0.24.html[2.0.24]
 * link:ReleaseNotes-2.0.23.html[2.0.23]
 * link:ReleaseNotes-2.0.22.html[2.0.22]
 * link:ReleaseNotes-2.0.21.html[2.0.21]
 * link:ReleaseNotes-2.0.20.html[2.0.20]
-* link:ReleaseNotes-2.0.19.html[2.0.19],
-  link:ReleaseNotes-2.0.19.html[2.0.19.1],
-  link:ReleaseNotes-2.0.19.html[2.0.19.2]
+* link:ReleaseNotes-2.0.19.html[2.0.19.2]
+* link:ReleaseNotes-2.0.19.html[2.0.19.1]
+* link:ReleaseNotes-2.0.19.html[2.0.19]
 * link:ReleaseNotes-2.0.18.html[2.0.18]
 * link:ReleaseNotes-2.0.17.html[2.0.17]
 * link:ReleaseNotes-2.0.16.html[2.0.16]
 * link:ReleaseNotes-2.0.15.html[2.0.15]
-* link:ReleaseNotes-2.0.14.html[2.0.14],
-  link:ReleaseNotes-2.0.14.html[2.0.14.1]
-* link:ReleaseNotes-2.0.13.html[2.0.13],
-  link:ReleaseNotes-2.0.13.html[2.0.13.1]
+* link:ReleaseNotes-2.0.14.html[2.0.14.1]
+* link:ReleaseNotes-2.0.14.html[2.0.14]
+* link:ReleaseNotes-2.0.13.html[2.0.13.1]
+* link:ReleaseNotes-2.0.13.html[2.0.13]
 * link:ReleaseNotes-2.0.12.html[2.0.12]
 * link:ReleaseNotes-2.0.11.html[2.0.11]
 * link:ReleaseNotes-2.0.10.html[2.0.10]
diff --git a/SUBMITTING_PATCHES b/SUBMITTING_PATCHES
index e766ef1..553ab34 100644
--- a/SUBMITTING_PATCHES
+++ b/SUBMITTING_PATCHES
@@ -5,7 +5,7 @@
  - Make sure all code is under the Apache License, 2.0.
  - Publish your changes for review:
 
-   git push https://gerrit-review.googlesource.com/gerrit HEAD:refs/for/master
+   git push https://gerrit.googlesource.com/gerrit HEAD:refs/for/master
 
 
 Long Version:
@@ -70,7 +70,7 @@
 Push your patches over HTTPS to the review server, possibly through
 a remembered remote to make this easier in the future:
 
-   git config remote.review.url https://google-review.googlesource.com/gerrit
+   git config remote.review.url https://gerrit.googlesource.com/gerrit
    git config remote.review.push HEAD:refs/for/master
 
    git push review
diff --git a/contrib/git-exproll.sh b/contrib/git-exproll.sh
new file mode 100644
index 0000000..9526d9f
--- /dev/null
+++ b/contrib/git-exproll.sh
@@ -0,0 +1,566 @@
+#!/bin/bash
+# Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#    # Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#    # Redistributions in binary form must reproduce the above
+#       copyright notice, this list of conditions and the following
+#       disclaimer in the documentation and/or other materials provided
+#       with the distribution.
+#    # Neither the name of Code Aurora Forum, Inc. nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+usage() { # error_message
+
+    cat <<-EOF
+		usage: $(basename $0) [-unvt] [--noref] [--nolosse] [-r|--ratio number]
+		                      [git gc option...] git.repo
+
+		-u|-h                usage/help
+		-v verbose
+		-n dry-run           don't actually repack anything
+		-t touch             treat repo as if it had been touched
+		--noref              avoid extra ref packing timestamp checking
+		--noloose            do not run just because there are loose object dirs
+		                     (repacking may still run if they are referenced)
+		-r ratio <number>    packfile ratio to aim for (default 10)
+
+		git gc option        will be passed as args to git gc
+
+		git.repo             to run gc against
+
+		Garbage collect using a pseudo logarithmic packfile maintenance
+		approach.  This approach attempts to minimize packfile churn
+		by keeping several generations of varying sized packfiles around
+		and only consolidating packfiles (or loose objects) which are
+		either new packfiles, or packfiles close to the same size as
+		another packfile.
+
+		An estimate is used to predict when rollups (one consolidation
+		would cause another consolidation) would occur so that this
+		rollup can be done all at once via a single repack.  This reduces
+		both the runtime and the pack file churn in rollup cases.
+
+		Approach: plan each consolidation by creating a table like this:
+
+		Id Keep Size           Sha1(or consolidation list)      Actions(repack down up note)
+		1     - 11356          9052edfb7392646cd4e5f362b953675985f01f96 y - - New
+		2     - 429088         010904d5c11cd26a79fda91b01ab454d1001b402 y - - New
+		c1    - 440444         [1,2]                                    - - -
+
+		Id:    numbers preceded by a c are estimated "c pack" files
+		Keep:  - none, k private keep, o our keep
+		Size:  in disk blocks (default du output)
+		Sha1:  of packfile, or consolidation list of packfile ids
+		Actions
+		repack: - n no, y yes
+		down:   - noop, ^ consolidate with a file above
+		up:     - noop, v consolidate with a file below
+		note:   Human description of script decisions:
+		         New (file is a new packfile)
+		         Consolidate with:<list of packfile ids>
+		         (too far from:<list of packfile ids>)
+
+		On the first pass, always consolidate any new packfiles along
+		with loose objects and along with any packfiles which are within
+		the ratio size of their predecessors (note, the list is ordered
+		by increasing size).  After each consolidation, insert a fake
+		consolidation, or "c pack", to naively represent the size and
+		ordered positioning of the anticipated new consolidated pack.
+		Every time a new pack is planned, rescan the list in case the
+		new "c pack" would cause more consolidation...
+
+		Once the packfiles which need consolidation are determined, the
+		packfiles which will not be consolidated are marked with a .keep
+		file, and those which will be consolidated will have their .keep
+		removed if they have one.  Thus, the packfiles with a .keep will
+		not get repacked.
+
+		Packfile consolidation is determined by the --ratio parameter
+		(default is 10).  This ratio is somewhat of a tradeoff.  The
+		smaller the number, the more packfiles will be kept on average;
+		this increases disk utilization somewhat.  However, a larger
+		ratio causes greater churn and may increase disk utilization due
+		to deleted packfiles not being reclaimed since they may still be
+		kept open by long running applications such as Gerrit.  Sane
+		ratio values are probably between 2 and 10.  Since most
+		consolidations actually end up smaller than the estimated
+		consolidated packfile size (due to compression), the true ratio
+		achieved will likely be 1 to 2 greater than the target ratio.
+		The smaller the target ratio, the greater this discrepancy.
+
+		Finally, attempt to skip garbage collection entirely on untouched
+		repos.  In order to determine if a repo has been touched, use the
+		timestamp on the script's keep files, if any relevant file/dir
+		is newer than a keep marker file, assume that the repo has been
+		touched and gc needs to run.  Also assume gc needs to run whenever
+		there are loose object dirs since they may contain untouched
+		unreferenced loose objects which need to be pruned (once they
+		expire).
+
+		In order to allow the keep files to be an effective timestamp
+		marker to detect relevant changes in a repo since the last run,
+		all relevant files and directories which may be modified during a
+		gc run (even during a noop gc run), must have their timestamps
+		reset to the same time as the keep files or gc will always run
+		even on untouched repos.  The relevant files/dirs are all those
+		files and directories which garbage collection, object packing,
+		ref packing and pruning might change during noop actions.
+EOF
+
+    [ -n "$1" ] && info "ERROR $1"
+
+    exit
+}
+
+debug() { [ -n "$SW_V" ] && info "$1" ; }
+info() { echo "$1" >&2 ; }
+
+array_copy() { #v2 # array_src array_dst
+    local src=$1 dst=$2
+    local s i=0
+    eval s=\${#$src[@]}
+    while [ $i -lt $s ] ; do
+        eval $dst[$i]=\"\${$src[$i]}\"
+        i=$(($i + 1))
+    done
+}
+
+array_equals() { #v2 # array_name [vals...]
+    local a=$1 ; shift
+    local s=0 t=() val
+    array_copy "$a" t
+    for s in "${!t[@]}" ; do s=$((s+1)) ; done
+    [ "$s" -ne "$#" ] && return 1
+    for val in "${t[@]}" ; do
+        [ "$val" = "$1" ] || return 2
+        shift
+    done
+    return 0
+}
+
+packs_sizes() { # git.repo > "size pack"...
+    du -s "$1"/objects/pack/pack-$SHA1.pack | sort -n 2> /dev/null
+}
+
+is_ourkeep() { grep -q "$KEEP" "$1" 2> /dev/null ; } # keep
+has_ourkeep() { is_ourkeep "$(keep_for "$1")" ; } # pack
+has_keep() { [ -f "$(keep_for "$1")" ] ; } # pack
+is_repo() { [ -d "$1/objects" ] && [ -d "$1/refs/heads" ] ; } # git.repo
+
+keep() { # pack   # returns true if we added our keep
+    keep=$(keep_for "$1")
+    [ -f "$keep" ] && return 1
+    echo "$KEEP" > "$keep"
+    return 0
+}
+
+keep_for() { # packfile > keepfile
+    local keep=$(echo "$1" | sed -es'/\.pack$/.keep/')
+    [ "${keep/.keep}" = "$keep" ] && return 1
+    echo "$keep"
+}
+
+idx_for() { # packfile > idxfile
+    local idx=$(echo "$1" | sed -es'/\.pack$/.idx/')
+    [ "${idx/.idx}" = "$idx" ] && return 1
+    echo "$idx"
+}
+
+# pack_or_keep_file > sha
+sha_for() { echo "$1" | sed -es'|\(.*/\)*pack-\([^.]*\)\..*$|\2|' ; }
+
+private_keeps() { # git.repo -> sets pkeeps
+    local repo=$1 ary=$2
+    local keep keeps=("$repo"/objects/pack/pack-$SHA1.keep)
+    pkeeps=()
+    for keep in "${keeps[@]}" ; do
+        is_ourkeep "$keep" || pkeeps=("${pkeeps[@]}" "$keep")
+    done
+}
+
+is_tooclose() { [ "$(($1 * $RATIO))" -gt "$2" ] ; } # smaller larger
+
+unique() { # [args...] > unique_words
+    local lines=$(while [ $# -gt 0 ] ; do echo "$1" ; shift ; done)
+    lines=$(echo "$lines" | sort -u)
+    echo $lines  # as words
+}
+
+outfs() { # fs [args...] > argfs...
+    local fs=$1 ; shift
+    [ $# -gt 0 ] && echo -n "$1" ; shift
+    while [ $# -gt 0 ] ; do echo -n "$fs$1" ; shift ; done
+}
+
+sort_list() { # < list > formatted_list
+    # n has_keep size sha repack down up note
+    awk '{ note=$8; for(i=8;i<NF;i++) note=note " "$(i+1)
+           printf("%-5s %s %-14s %-40s %s %s %s %s\n", \
+                     $1,$2,   $3,  $4, $5,$6,$7,note)}' |\
+        sort -k 3,3n -k 1,1n
+}
+
+is_touched() { # git.repo
+    local repo=$1
+    local loose keep ours newer
+    [ -n "$SW_T" ] && { debug "$SW_T -> treat as touched" ; return 0 ; }
+
+    if [ -z "$SW_LOOSE" ] ; then
+        # If there are loose objects, they may need to be pruned,
+        # run even if nothing has really been touched.
+        loose=$(find "$repo/objects" -type d \
+                      -wholename "$repo/objects/[0-9][0-9]"
+                      -print -quit 2>/dev/null)
+        [ -n "$loose" ] && { info "There are loose object directories" ; return 0 ; }
+    fi
+
+    # If we don't have a keep, the current packfiles may not have been
+    # compressed with the current gc policy (gc may never have been run),
+    # so run at least once to repack everything.  Also, we need a marker
+    # file for timestamp tracking (a dir needs to detect changes within
+    # it, so it cannot be a marker) and our keeps are something we control,
+    # use them.
+    for keep in "$repo"/objects/pack/pack-$SHA1.keep ; do
+        is_ourkeep "$keep" && { ours=$keep ; break ; }
+    done
+    [ -z "$ours" ] && { info 'We have no keep (we have never run?): run' ; return 0 ; }
+
+    debug "Our timestamp keep: $ours"
+    # The wholename stuff seems to get touched by a noop git gc
+    newer=$(find "$repo/objects" "$repo/refs" "$repo/packed-refs" \
+                  '!' -wholename "$repo/objects/info" \
+                  '!' -wholename "$repo/objects/info/*" \
+                  -newer "$ours" \
+                  -print -quit 2>/dev/null)
+    [ -z "$newer" ] && return 1
+
+    info "Touched since last run: $newer"
+    return 0
+}
+
+touch_refs() { # git.repo start_date refs
+    local repo=$1 start_date=$2 refs=$3
+    (
+        debug "Setting start date($start_date) on unpacked refs:"
+        debug "$refs"
+        cd "$repo/refs" || return
+        # safe to assume no newlines in a ref name
+        echo "$refs" | xargs -d '\n' -n 1 touch -c -d "$start_date"
+    )
+}
+
+set_start_date() { # git.repo start_date refs refdirs packedrefs [packs]
+    local repo=$1 start_date=$2 refs=$3 refdirs=$4 packedrefs=$5 ; shift 5
+    local pack keep idx repacked
+
+    # This stuff is touched during object packs
+    while [ $# -gt 0 ] ; do
+        pack=$1 ; shift
+        keep="$(keep_for "$pack")"
+        idx="$(idx_for "$pack")"
+        touch -c -d "$start_date" "$pack" "$keep" "$idx"
+        debug "Setting start date on: $pack $keep $idx"
+    done
+    # This will prevent us from detecting any deletes in the pack dir
+    # since gc ran, except for private keeps which we are checking
+    # manually.  But there really shouldn't be any other relevant deletes
+    # in this dir which should cause us to rerun next time, deleting a
+    # pack or index file by anything but gc would be bad!
+    debug "Setting start date on pack dir: $start_date"
+    touch -c -d "$start_date" "$repo/objects/pack"
+
+
+    if [ -z "$SW_REFS" ] ; then
+        repacked=$(find "$repo/packed-refs" -newer "$repo/objects/pack"
+                      -print -quit 2>/dev/null)
+        if [ -n "$repacked" ] ; then
+            # The ref dirs and packed-ref files seem to get touched even on
+            # a noop refpacking
+            debug "Setting start date on packed-refs"
+            touch -c -d "$start_date" "$repo/packed-refs"
+            touch_refs "$repo" "$start_date" "$refdirs"
+
+            # A ref repack does not imply a ref change, but since it is
+            # hard to tell, simply assume so
+            if [ "$refs" != "$(cd "$repo/refs" ; find -depth)" ] || \
+               [ "$packedrefs" != "$(<"$repo/packed-refs")" ] ; then
+                # We retouch if needed (instead of simply checking then
+                # touching) to avoid a race between the check and the set.
+                debug "  but refs actually got packed, so retouch packed-refs"
+                touch -c "$repo/packed-refs"
+            fi
+        fi
+    fi
+}
+
+note_consolidate() { # note entry > note (no duplicated consolidated entries)
+    local note=$1 entry=$2
+    local entries=() ifs=$IFS
+    if  echo "$note" | grep -q 'Consolidate with:[0-9,c]' ; then
+        IFS=,
+        entries=( $(echo "$note" | sed -es'/^.*Consolidate with:\([0-9,c]*\).*$/\1/') )
+        note=( $(echo "$note" | sed -es'/Consolidate with:[0-9,c]*//') )
+        IFS=$ifs
+    fi
+    entries=( $(unique "${entries[@]}" "$entry") )
+    echo "$note Consolidate with:$(outfs , "${entries[@]}")"
+}
+
+note_toofar() { # note entry > note (no duplicated "too far" entries)
+    local note=$1 entry=$2
+    local entries=() ifs=$IFS
+    if  echo "$note" | grep -q '(too far from:[0-9,c]*)' ; then
+        IFS=,
+        entries=( $(echo "$note" | sed -es'/^.*(too far from:\([0-9,c]*\)).*$/\1/') )
+        note=( $(echo "$note" | sed -es'/(too far from:[0-9,c]*)//') )
+        IFS=$ifs
+    fi
+    entries=( $(unique "${entries[@]}" "$entry") )
+    echo "$note (too far from:$(outfs , "${entries[@]}"))"
+}
+
+last_entry() { # isRepack pline repackline > last_rows_entry
+    local size_hit=$1 pline=$2 repackline=$3
+    if [ -n "$pline" ] ; then
+        if [ -n "$size_hit" ] ; then
+            echo "$repack_line"
+        else
+            echo "$pline"
+        fi
+    fi
+}
+
+init_list() { # git.repo > shortlist
+    local repo=$1
+    local file
+    local n has_keep size sha repack
+
+    packs_sizes "$1" | {
+        while read size file ; do
+            n=$((n+1))
+            repack=n
+            has_keep=-
+            if has_keep "$file" ; then
+                has_keep=k
+                has_ourkeep "$file" && has_keep=o
+            fi
+            sha=$(sha_for "$file")
+            echo "$n $has_keep $size $sha $repack"
+        done
+    } | sort_list
+}
+
+consolidate_list() { # run < list > list
+    local run=$1
+    local sum=0 psize=0 sum_size=0 size_hit pn clist pline repackline
+    local n has_keep size sha repack down up note
+
+    {
+        while read n has_keep size sha repack down up note; do
+            [ -z "$up" ] && up='-'
+            [ -z "$down" ] && down="-"
+
+            if [ "$has_keep" = "k" ] ; then
+                echo "$n $has_keep $size $sha $repack - - Private"
+                continue
+            fi
+
+            if [ "$repack" = "n" ] ; then
+                if is_tooclose $psize $size ; then
+                    size_hit=y
+                    repack=y
+                    sum=$(($sum + $sum_size + $size))
+                    sum_size=0 # Prevents double summing this entry
+                    clist=($(unique "${clist[@]}" $pn $n))
+                    down="^"
+                    [ "$has_keep" = "-" ] && note="$note New +"
+                    note=$(note_consolidate "$note" "$pn")
+                elif [ "$has_keep" = "-" ] ; then
+                    repack=y
+                    sum=$(($sum + $size))
+                    sum_size=0 # Prevents double summing this entry
+                    clist=($(unique "${clist[@]}" $n))
+                    note="$note New"
+                elif [ $psize -ne 0 ] ; then
+                    sum_size=$size
+                    down="!"
+                    note=$(note_toofar "$note" "$pn")
+                else
+                    sum_size=$size
+                fi
+            else
+                sum_size=$size
+            fi
+
+            # By preventing "c files" (consolidated) from being marked
+            # "repack" they won't get keeps
+            repack2=y
+            [ "${n/c}" != "$n" ] && { repack=- ; repack2=- ; }
+
+            last_entry "$size_hit" "$pline" "$repack_line"
+            # Delay the printout until we know whether we are
+            # being consolidated with the entry following us
+            # (we won't know until the next iteration).
+            # size_hit is used to determine which of the lines
+            # below will actually get printed above on the next
+            # iteration.
+            pline="$n $has_keep $size $sha $repack $down $up $note"
+            repack_line="$n $has_keep $size $sha $repack2 $down v $note"
+
+            pn=$n ; psize=$size # previous entry data
+            size_hit='' # will not be consolidated up
+
+        done
+        last_entry "$size_hit" "$pline" "$repack_line"
+
+        [ $sum -gt 0 ] && echo "c$run - $sum [$(outfs , "${clist[@]}")] - - -"
+
+    } | sort_list
+}
+
+process_list() { # git.repo > list
+    local list=$(init_list "$1")  plist run=0
+
+    while true ; do
+        plist=$list
+        run=$((run +1))
+        list=$(echo "$list" | consolidate_list "$run")
+        if [ "$plist" != "$list" ] ; then
+            debug "------------------------------------------------------------------------------------"
+            debug "$HEADER"
+            debug "$list"
+        else
+            break
+        fi
+    done
+    debug "------------------------------------------------------------------------------------"
+    echo "$list"
+}
+
+repack_list() { # git.repo < list
+    local repo=$1
+    local start_date newpacks=0 pkeeps keeps=1 refs refdirs rtn
+    local packedrefs=$(<"$repo/packed-refs")
+
+    # so they don't appear touched after a noop refpacking
+    if [ -z "$SW_REFS" ] ; then
+        refs=$(cd "$repo/refs" ; find -depth)
+        refdirs=$(cd "$repo/refs" ; find -type d -depth)
+        debug "Before refs:"
+        debug "$refs"
+    fi
+
+    # Find a private keep snapshot which has not changed from
+    # before our start_date so private keep deletions during gc
+    # can be detected
+    while ! array_equals pkeeps "${keeps[@]}" ; do
+       debug "Getting a private keep snapshot"
+       private_keeps "$repo"
+       keeps=("${pkeeps[@]}")
+       debug "before keeps: ${keeps[*]}"
+       start_date=$(date)
+       private_keeps "$repo"
+       debug "after keeps: ${pkeeps[*]}"
+    done
+
+    while read n has_keep size sha repack down up note; do
+        if [ "$repack" = "y" ] ; then
+            keep="$repo/objects/pack/pack-$sha.keep"
+            info "Repacking $repo/objects/pack/pack-$sha.pack"
+            [ -f "$keep" ] && rm -f "$keep"
+        fi
+    done
+
+    ( cd "$repo" && git gc "${GC_OPTS[@]}" ) ; rtn=$?
+
+    # Mark any files withoug a .keep with our .keep
+    packs=("$repo"/objects/pack/pack-$SHA1.pack)
+    for pack in "${packs[@]}" ; do
+        if keep "$pack" ; then
+            info "New pack: $pack"
+            newpacks=$((newpacks+1))
+        fi
+    done
+
+    # Record start_time.  If there is more than 1 new packfile, we
+    # don't want to risk touching it with an older date since that
+    # would prevent consolidation on the next run.  If the private
+    # keeps have changed, then we should run next time no matter what.
+    if [ $newpacks -le 1 ] || ! array_equals pkeeps "${keeps[@]}" ; then
+        set_start_date "$repo" "$start_date" "$refs" "$refdirs" "$packedrefs" "${packs[@]}"
+    fi
+
+    return $rtn # we really only care about the gc error code
+}
+
+git_gc() { # git.repo
+    local list=$(process_list "$1")
+    if [ -z "$SW_V" ] ; then
+        info "Running $PROG on $1.  git gc options: ${GC_OPTS[@]}"
+        echo "$HEADER" >&2
+        echo "$list" >&2 ;
+    fi
+    echo "$list" | repack_list "$1"
+}
+
+
+PROG=$(basename "$0")
+HEADER="Id Keep Size           Sha1(or consolidation list)      Actions(repack down up note)"
+KEEP=git-exproll
+HEX='[0-9a-f]'
+HEX10=$HEX$HEX$HEX$HEX$HEX$HEX$HEX$HEX$HEX$HEX
+SHA1=$HEX10$HEX10$HEX10$HEX10
+
+RATIO=10
+SW_N='' ; SW_V='' ; SW_T='' ; SW_REFS='' ; SW_LOOSE='' ; GC_OPTS=()
+while [ $# -gt 0 ] ; do
+    case "$1" in
+        -u|-h)  usage ;;
+        -n)  SW_N="$1" ;;
+        -v)  SW_V="$1" ;;
+
+        -t)  SW_T="$1" ;;
+        --norefs)  SW_REFS="$1" ;;
+        --noloose) SW_LOOSE="$1" ;;
+
+        -r|--ratio)  shift ; RATIO="$1" ;;
+
+        *)  [ $# -le 1 ] && break
+            GC_OPTS=( "${GC_OPTS[@]}" "$1" )
+            ;;
+    esac
+    shift
+done
+
+
+REPO="$1"
+if ! is_repo "$REPO" ; then
+    REPO=$REPO/.git
+    is_repo "$REPO" || usage "($1) is not likely a git repo"
+fi
+
+
+if [ -z "$SW_N" ] ; then
+    is_touched "$REPO" || { info "Repo untouched since last run" ; exit ; }
+    git_gc "$REPO"
+else
+    is_touched "$REPO" || info "Repo untouched since last run, analyze anyway."
+    process_list "$REPO" >&2
+fi
diff --git a/gerrit-antlr/.gitignore b/gerrit-antlr/.gitignore
index 194bedc..fb047af 100644
--- a/gerrit-antlr/.gitignore
+++ b/gerrit-antlr/.gitignore
@@ -2,4 +2,5 @@
 /.classpath
 /.project
 /.settings/org.maven.ide.eclipse.prefs
-/.settings/org.eclipse.m2e.core.prefs
\ No newline at end of file
+/.settings/org.eclipse.m2e.core.prefs
+/gerrit-antlr.iml
\ No newline at end of file
diff --git a/gerrit-antlr/.settings/org.eclipse.core.resources.prefs b/gerrit-antlr/.settings/org.eclipse.core.resources.prefs
index 589908f..e9441bb 100644
--- a/gerrit-antlr/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-antlr/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:35 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding/<project>=UTF-8
diff --git a/gerrit-antlr/pom.xml b/gerrit-antlr/pom.xml
index aa0d7fd..34cb46f 100644
--- a/gerrit-antlr/pom.xml
+++ b/gerrit-antlr/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.4-SNAPSHOT</version>
+    <version>2.5-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-antlr</artifactId>
diff --git a/gerrit-ehcache/.gitignore b/gerrit-cache-h2/.gitignore
similarity index 83%
copy from gerrit-ehcache/.gitignore
copy to gerrit-cache-h2/.gitignore
index 20251d4..cb430b8 100644
--- a/gerrit-ehcache/.gitignore
+++ b/gerrit-cache-h2/.gitignore
@@ -1,5 +1,6 @@
 /target
 /.classpath
 /.project
-/.settings/org.eclipse.m2e.core.prefs
 /.settings/org.maven.ide.eclipse.prefs
+/.settings/org.eclipse.m2e.core.prefs
+/gerrit-cache-h2.iml
diff --git a/gerrit-cache-h2/.settings/org.eclipse.core.resources.prefs b/gerrit-cache-h2/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..f9fe345
--- /dev/null
+++ b/gerrit-cache-h2/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/test/java=UTF-8
+encoding/<project>=UTF-8
diff --git a/gerrit-ehcache/.settings/org.eclipse.core.runtime.prefs b/gerrit-cache-h2/.settings/org.eclipse.core.runtime.prefs
similarity index 100%
rename from gerrit-ehcache/.settings/org.eclipse.core.runtime.prefs
rename to gerrit-cache-h2/.settings/org.eclipse.core.runtime.prefs
diff --git a/gerrit-ehcache/.settings/org.eclipse.jdt.core.prefs b/gerrit-cache-h2/.settings/org.eclipse.jdt.core.prefs
similarity index 99%
rename from gerrit-ehcache/.settings/org.eclipse.jdt.core.prefs
rename to gerrit-cache-h2/.settings/org.eclipse.jdt.core.prefs
index e89c048..470942d 100644
--- a/gerrit-ehcache/.settings/org.eclipse.jdt.core.prefs
+++ b/gerrit-cache-h2/.settings/org.eclipse.jdt.core.prefs
@@ -1,4 +1,4 @@
-#Thu Jan 19 12:55:44 PST 2012
+#Thu Jul 28 11:02:36 PDT 2011
 eclipse.preferences.version=1
 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
diff --git a/gerrit-ehcache/.settings/org.eclipse.jdt.ui.prefs b/gerrit-cache-h2/.settings/org.eclipse.jdt.ui.prefs
similarity index 100%
rename from gerrit-ehcache/.settings/org.eclipse.jdt.ui.prefs
rename to gerrit-cache-h2/.settings/org.eclipse.jdt.ui.prefs
diff --git a/gerrit-ehcache/pom.xml b/gerrit-cache-h2/pom.xml
similarity index 73%
rename from gerrit-ehcache/pom.xml
rename to gerrit-cache-h2/pom.xml
index 839c52b0..4d4303c 100644
--- a/gerrit-ehcache/pom.xml
+++ b/gerrit-cache-h2/pom.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-Copyright (C) 2010 The Android Open Source Project
+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.
@@ -22,26 +22,31 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.4-SNAPSHOT</version>
+    <version>2.5-SNAPSHOT</version>
   </parent>
 
-  <artifactId>gerrit-ehcache</artifactId>
-  <name>Gerrit Code Review - Ehcache Bindings</name>
+  <artifactId>gerrit-cache-h2</artifactId>
+  <name>Gerrit Code Review - Guava + H2 caching</name>
 
   <description>
-    Bindings to Ehcache
+    Implementation of caching backed by Guava and H2
   </description>
 
   <dependencies>
     <dependency>
-      <groupId>net.sf.ehcache</groupId>
-      <artifactId>ehcache-core</artifactId>
-    </dependency>
-
-    <dependency>
       <groupId>com.google.gerrit</groupId>
       <artifactId>gerrit-server</artifactId>
       <version>${project.version}</version>
     </dependency>
+
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.h2database</groupId>
+      <artifactId>h2</artifactId>
+    </dependency>
   </dependencies>
 </project>
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
new file mode 100644
index 0000000..5a600c0
--- /dev/null
+++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/DefaultCacheFactory.java
@@ -0,0 +1,135 @@
+// 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.server.cache.h2;
+
+import com.google.common.base.Strings;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.cache.Weigher;
+import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.gerrit.server.cache.CacheBinding;
+import com.google.gerrit.server.cache.ForwardingRemovalListener;
+import com.google.gerrit.server.cache.MemoryCacheFactory;
+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;
+
+import org.eclipse.jgit.lib.Config;
+
+import java.util.concurrent.TimeUnit;
+
+public class DefaultCacheFactory implements MemoryCacheFactory {
+  public static class Module extends LifecycleModule {
+    @Override
+    protected void configure() {
+      install(new FactoryModule() {
+        @Override
+        protected void configure() {
+          factory(ForwardingRemovalListener.Factory.class);
+        }
+      });
+
+      bind(DefaultCacheFactory.class);
+      bind(MemoryCacheFactory.class).to(DefaultCacheFactory.class);
+      bind(PersistentCacheFactory.class).to(H2CacheFactory.class);
+      listener().to(H2CacheFactory.class);
+    }
+  }
+
+  private final Config cfg;
+  private final ForwardingRemovalListener.Factory forwardingRemovalListenerFactory;
+
+  @Inject
+  public DefaultCacheFactory(@GerritServerConfig Config config,
+      ForwardingRemovalListener.Factory forwardingRemovalListenerFactory) {
+    this.cfg = config;
+    this.forwardingRemovalListenerFactory = forwardingRemovalListenerFactory;
+  }
+
+  @Override
+  public <K, V> Cache<K, V> build(CacheBinding<K, V> def) {
+    return create(def, false).build();
+  }
+
+  @Override
+  public <K, V> LoadingCache<K, V> build(
+      CacheBinding<K, V> def,
+      CacheLoader<K, V> loader) {
+    return create(def, false).build(loader);
+  }
+
+  @SuppressWarnings("unchecked")
+  <K, V> CacheBuilder<K, V> create(
+      CacheBinding<K, V> def,
+      boolean unwrapValueHolder) {
+    CacheBuilder<K,V> builder = newCacheBuilder();
+    builder.recordStats();
+    builder.maximumWeight(cfg.getLong(
+        "cache", def.name(), "memoryLimit",
+        def.maximumWeight()));
+
+    builder.removalListener(forwardingRemovalListenerFactory.create(def.name()));
+
+    Weigher<K, V> weigher = def.weigher();
+    if (weigher != null && unwrapValueHolder) {
+      final Weigher<K, V> impl = weigher;
+      weigher = (Weigher<K, V>) new Weigher<K, ValueHolder<V>> () {
+        @Override
+        public int weigh(K key, ValueHolder<V> value) {
+          return impl.weigh(key, value.value);
+        }
+      };
+    } else if (weigher == null) {
+      weigher = unitWeight();
+    }
+    builder.weigher(weigher);
+
+    Long age = def.expireAfterWrite(TimeUnit.SECONDS);
+    if (has(def.name(), "maxAge")) {
+      builder.expireAfterWrite(ConfigUtil.getTimeUnit(cfg,
+          "cache", def.name(), "maxAge",
+          age != null ? age : 0,
+          TimeUnit.SECONDS), TimeUnit.SECONDS);
+    } else if (age != null) {
+      builder.expireAfterWrite(age, TimeUnit.SECONDS);
+    }
+
+    return builder;
+  }
+
+  private boolean has(String name, String var) {
+    return !Strings.isNullOrEmpty(cfg.getString("cache", name, var));
+  }
+
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  private static <K, V> CacheBuilder<K, V> newCacheBuilder() {
+    CacheBuilder builder = CacheBuilder.newBuilder();
+    return builder;
+  }
+
+  private static <K, V> Weigher<K, V> unitWeight() {
+    return new Weigher<K, V>() {
+      @Override
+      public int weigh(K key, V value) {
+        return 1;
+      }
+    };
+  }
+}
diff --git a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
new file mode 100644
index 0000000..27da20f
--- /dev/null
+++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
@@ -0,0 +1,198 @@
+// 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.server.cache.h2;
+
+import com.google.common.base.Preconditions;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.server.cache.CacheBinding;
+import com.google.gerrit.server.cache.PersistentCacheFactory;
+import com.google.gerrit.server.cache.h2.H2CacheImpl.SqlStore;
+import com.google.gerrit.server.cache.h2.H2CacheImpl.ValueHolder;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+
+import org.eclipse.jgit.lib.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+@Singleton
+class H2CacheFactory implements PersistentCacheFactory, LifecycleListener {
+  static final Logger log = LoggerFactory.getLogger(H2CacheFactory.class);
+
+  private final DefaultCacheFactory defaultFactory;
+  private final Config config;
+   private final File cacheDir;
+  private final List<H2CacheImpl<?, ?>> caches;
+  private final ExecutorService executor;
+  private final ScheduledExecutorService cleanup;
+  private volatile boolean started;
+
+  @Inject
+  H2CacheFactory(
+      DefaultCacheFactory defaultCacheFactory,
+      @GerritServerConfig Config cfg,
+      SitePaths site) {
+    defaultFactory = defaultCacheFactory;
+    config = cfg;
+
+    File loc = site.resolve(cfg.getString("cache", null, "directory"));
+    if (loc == null) {
+      cacheDir = null;
+    } else if (loc.exists() || loc.mkdirs()) {
+      if (loc.canWrite()) {
+        log.info("Enabling disk cache " + loc.getAbsolutePath());
+        cacheDir = loc;
+      } else {
+        log.warn("Can't write to disk cache: " + loc.getAbsolutePath());
+        cacheDir = null;
+      }
+    } else {
+      log.warn("Can't create disk cache: " + loc.getAbsolutePath());
+      cacheDir = null;
+    }
+
+    caches = Lists.newLinkedList();
+
+    if (cacheDir != null) {
+      executor = Executors.newFixedThreadPool(
+          1,
+          new ThreadFactoryBuilder()
+            .setNameFormat("DiskCache-Store-%d")
+            .build());
+      cleanup = Executors.newScheduledThreadPool(
+          1,
+          new ThreadFactoryBuilder()
+            .setNameFormat("DiskCache-Prune-%d")
+            .setDaemon(true)
+            .build());
+    } else {
+      executor = null;
+      cleanup = null;
+    }
+  }
+
+  @Override
+  public void start() {
+    started = true;
+    if (executor != null) {
+      for (final H2CacheImpl<?, ?> cache : caches) {
+        executor.execute(new Runnable() {
+          @Override
+          public void run() {
+            cache.start();
+          }
+        });
+
+        cleanup.schedule(new Runnable() {
+          @Override
+          public void run() {
+            cache.prune(cleanup);
+          }
+        }, 30, TimeUnit.SECONDS);
+      }
+    }
+  }
+
+  @Override
+  public void stop() {
+    if (executor != null) {
+      try {
+        cleanup.shutdownNow();
+
+        List<Runnable> pending = executor.shutdownNow();
+        if (executor.awaitTermination(15, TimeUnit.MINUTES)) {
+          if (pending != null && !pending.isEmpty()) {
+            log.info(String.format("Finishing %d disk cache updates", pending.size()));
+            for (Runnable update : pending) {
+              update.run();
+            }
+          }
+        } else {
+          log.info("Timeout waiting for disk cache to close");
+        }
+      } catch (InterruptedException e) {
+        log.warn("Interrupted waiting for disk cache to shutdown");
+      }
+    }
+    for (H2CacheImpl<?, ?> cache : caches) {
+      cache.stop();
+    }
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes", "cast"})
+  @Override
+  public <K, V> Cache<K, V> build(CacheBinding<K, V> def) {
+    Preconditions.checkState(!started, "cache must be built before start");
+    long limit = config.getLong("cache", def.name(), "diskLimit", 128 << 20);
+
+    if (cacheDir == null || limit <= 0) {
+      return defaultFactory.build(def);
+    }
+
+    SqlStore<K, V> store = newSqlStore(def.name(), def.keyType(), limit);
+    H2CacheImpl<K, V> cache = new H2CacheImpl<K, V>(
+        executor, store, def.keyType(),
+        (Cache<K, ValueHolder<V>>) defaultFactory.create(def, true).build());
+    caches.add(cache);
+    return cache;
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public <K, V> LoadingCache<K, V> build(
+      CacheBinding<K, V> def,
+      CacheLoader<K, V> loader) {
+    Preconditions.checkState(!started, "cache must be built before start");
+    long limit = config.getLong("cache", def.name(), "diskLimit", 128 << 20);
+
+    if (cacheDir == null || limit <= 0) {
+      return defaultFactory.build(def, loader);
+    }
+
+    SqlStore<K, V> store = newSqlStore(def.name(), def.keyType(), limit);
+    Cache<K, ValueHolder<V>> mem = (Cache<K, ValueHolder<V>>)
+        defaultFactory.create(def, true)
+        .build((CacheLoader<K, V>) new H2CacheImpl.Loader<K, V>(
+              executor, store, loader));
+    H2CacheImpl<K, V> cache = new H2CacheImpl<K, V>(
+        executor, store, def.keyType(), mem);
+    caches.add(cache);
+    return cache;
+  }
+
+  private <V, K> SqlStore<K, V> newSqlStore(
+      String name,
+      TypeLiteral<K> keyType,
+      long maxSize) {
+    File db = new File(cacheDir, name).getAbsoluteFile();
+    String url = "jdbc:h2:" + db.toURI().toString();
+    return new SqlStore<K, V>(url, keyType, maxSize);
+  }
+}
diff --git a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
new file mode 100644
index 0000000..a196b07
--- /dev/null
+++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
@@ -0,0 +1,726 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package com.google.gerrit.server.cache.h2;
+
+import com.google.common.cache.AbstractLoadingCache;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.CacheStats;
+import com.google.common.cache.LoadingCache;
+import com.google.common.hash.BloomFilter;
+import com.google.common.hash.Funnel;
+import com.google.common.hash.Funnels;
+import com.google.common.hash.PrimitiveSink;
+import com.google.inject.TypeLiteral;
+
+import org.h2.jdbc.JdbcSQLException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Map;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Hybrid in-memory and database backed cache built on H2.
+ * <p>
+ * This cache can be used as either a recall cache, or a loading cache if a
+ * CacheLoader was supplied to its constructor at build time. Before creating an
+ * entry the in-memory cache is checked for the item, then the database is
+ * checked, and finally the CacheLoader is used to construct the item. This is
+ * mostly useful for CacheLoaders that are computationally intensive, such as
+ * the PatchListCache.
+ * <p>
+ * Cache stores and invalidations are performed on a background thread, hiding
+ * the latency associated with serializing the key and value pairs and writing
+ * them to the database log.
+ * <p>
+ * A BloomFilter is used around the database to reduce the number of SELECTs
+ * issued against the database for new cache items that have not been seen
+ * before, a common operation for the PatchListCache. The BloomFilter is sized
+ * when the cache starts to be 64,000 entries or double the number of items
+ * currently in the database table.
+ * <p>
+ * This cache does not export its items as a ConcurrentMap.
+ *
+ * @see H2CacheFactory
+ */
+public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> {
+  private static final Logger log = LoggerFactory.getLogger(H2CacheImpl.class);
+
+  private final Executor executor;
+  private final SqlStore<K, V> store;
+  private final TypeLiteral<K> keyType;
+  private final Cache<K, ValueHolder<V>> mem;
+
+  H2CacheImpl(Executor executor,
+      SqlStore<K, V> store,
+      TypeLiteral<K> keyType,
+      Cache<K, ValueHolder<V>> mem) {
+    this.executor = executor;
+    this.store = store;
+    this.keyType = keyType;
+    this.mem = mem;
+  }
+
+  @Override
+  public V getIfPresent(Object objKey) {
+    if (!keyType.getRawType().isInstance(objKey)) {
+      return null;
+    }
+
+    @SuppressWarnings("unchecked")
+    K key = (K) objKey;
+
+    ValueHolder<V> h = mem.getIfPresent(key);
+    if (h != null) {
+      return h.value;
+    }
+
+    if (store.mightContain(key)) {
+      h = store.getIfPresent(key);
+      if (h != null) {
+        mem.put(key, h);
+        return h.value;
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public V get(K key) throws ExecutionException {
+    if (mem instanceof LoadingCache) {
+      return ((LoadingCache<K, ValueHolder<V>>) mem).get(key).value;
+    }
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void put(final K key, V val) {
+    final ValueHolder<V> h = new ValueHolder<V>(val);
+    h.created = System.currentTimeMillis();
+    mem.put(key, h);
+    executor.execute(new Runnable() {
+      @Override
+      public void run() {
+        store.put(key, h);
+      }
+    });
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public void invalidate(final Object key) {
+    if (keyType.getRawType().isInstance(key) && store.mightContain((K) key)) {
+      executor.execute(new Runnable() {
+        @Override
+        public void run() {
+          store.invalidate((K) key);
+        }
+      });
+    }
+    mem.invalidate(key);
+  }
+
+  @Override
+  public void invalidateAll() {
+    store.invalidateAll();
+    mem.invalidateAll();
+  }
+
+  @Override
+  public long size() {
+    return mem.size();
+  }
+
+  @Override
+  public CacheStats stats() {
+    return mem.stats();
+  }
+
+  public DiskStats diskStats() {
+    return store.diskStats();
+  }
+
+  void start() {
+    store.open();
+  }
+
+  void stop() {
+    for (Map.Entry<K, ValueHolder<V>> e : mem.asMap().entrySet()) {
+      ValueHolder<V> h = e.getValue();
+      if (!h.clean) {
+        store.put(e.getKey(), h);
+      }
+    }
+    store.close();
+  }
+
+  void prune(final ScheduledExecutorService service) {
+    store.prune(mem);
+
+    Calendar cal = Calendar.getInstance();
+    cal.set(Calendar.HOUR_OF_DAY, 01);
+    cal.set(Calendar.MINUTE, 0);
+    cal.set(Calendar.SECOND, 0);
+    cal.set(Calendar.MILLISECOND, 0);
+    cal.add(Calendar.DAY_OF_MONTH, 1);
+
+    long delay = cal.getTimeInMillis() - System.currentTimeMillis();
+    service.schedule(new Runnable() {
+      @Override
+      public void run() {
+        prune(service);
+      }
+    }, delay, TimeUnit.MILLISECONDS);
+  }
+
+  public static class DiskStats {
+    long size;
+    long space;
+    long hitCount;
+    long missCount;
+
+    public long size() {
+      return size;
+    }
+
+    public long space() {
+      return space;
+    }
+
+    public long hitCount() {
+      return hitCount;
+    }
+
+    public long requestCount() {
+      return hitCount + missCount;
+    }
+  }
+
+  static class ValueHolder<V> {
+    final V value;
+    long created;
+    volatile boolean clean;
+
+    ValueHolder(V value) {
+      this.value = value;
+    }
+  }
+
+  static class Loader<K, V> extends CacheLoader<K, ValueHolder<V>> {
+    private final Executor executor;
+    private final SqlStore<K, V> store;
+    private final CacheLoader<K, V> loader;
+
+    Loader(Executor executor, SqlStore<K, V> store, CacheLoader<K, V> loader) {
+      this.executor = executor;
+      this.store = store;
+      this.loader = loader;
+    }
+
+    @Override
+    public ValueHolder<V> load(final K key) throws Exception {
+      if (store.mightContain(key)) {
+        ValueHolder<V> h = store.getIfPresent(key);
+        if (h != null) {
+          return h;
+        }
+      }
+
+      final ValueHolder<V> h = new ValueHolder<V>(loader.load(key));
+      h.created = System.currentTimeMillis();
+      executor.execute(new Runnable() {
+        @Override
+        public void run() {
+          store.put(key, h);
+        }
+      });
+      return h;
+    }
+  }
+
+  private static class KeyType<K> {
+    String columnType() {
+      return "OTHER";
+    }
+
+    @SuppressWarnings("unchecked")
+    K get(ResultSet rs, int col) throws SQLException {
+      return (K) rs.getObject(col);
+    }
+
+    void set(PreparedStatement ps, int col, K value) throws SQLException {
+      ps.setObject(col, value);
+    }
+
+    Funnel<K> funnel() {
+      return new Funnel<K>() {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public void funnel(K from, PrimitiveSink into) {
+          try {
+            ObjectOutputStream ser =
+                new ObjectOutputStream(new SinkOutputStream(into));
+            try {
+              ser.writeObject(from);
+              ser.flush();
+            } finally {
+              ser.close();
+            }
+          } catch (IOException err) {
+            throw new RuntimeException("Cannot hash as Serializable", err);
+          }
+        }
+      };
+    }
+
+    @SuppressWarnings("unchecked")
+    static <K> KeyType<K> create(TypeLiteral<K> type) {
+      if (type.getRawType() == String.class) {
+        return (KeyType<K>) STRING;
+      }
+      return (KeyType<K>) OTHER;
+    }
+
+    static final KeyType<?> OTHER = new KeyType<Object>();
+    static final KeyType<String> STRING = new KeyType<String>() {
+      @Override
+      String columnType() {
+        return "VARCHAR(4096)";
+      }
+
+      @Override
+      String get(ResultSet rs, int col) throws SQLException {
+        return rs.getString(col);
+      }
+
+      @Override
+      void set(PreparedStatement ps, int col, String value)
+          throws SQLException {
+        ps.setString(col, value);
+      }
+
+      @SuppressWarnings("unchecked")
+      @Override
+      Funnel<String> funnel() {
+        Funnel<?> s = Funnels.stringFunnel();
+        return (Funnel<String>) s;
+      }
+    };
+  }
+
+  static class SqlStore<K, V> {
+    private final String url;
+    private final KeyType<K> keyType;
+    private final long maxSize;
+    private final BlockingQueue<SqlHandle> handles;
+    private final AtomicLong hitCount = new AtomicLong();
+    private final AtomicLong missCount = new AtomicLong();
+    private volatile BloomFilter<K> bloomFilter;
+    private int estimatedSize;
+
+    SqlStore(String jdbcUrl, TypeLiteral<K> keyType, long maxSize) {
+      this.url = jdbcUrl;
+      this.keyType = KeyType.create(keyType);
+      this.maxSize = maxSize;
+
+      int cores = Runtime.getRuntime().availableProcessors();
+      int keep = Math.min(cores, 16);
+      this.handles = new ArrayBlockingQueue<SqlHandle>(keep);
+    }
+
+    synchronized void open() {
+      if (bloomFilter == null) {
+        bloomFilter = buildBloomFilter();
+      }
+    }
+
+    void close() {
+      SqlHandle h;
+      while ((h = handles.poll()) != null) {
+        h.close();
+      }
+    }
+
+    boolean mightContain(K key) {
+      BloomFilter<K> b = bloomFilter;
+      if (b == null) {
+        synchronized (this) {
+          b = bloomFilter;
+          if (b == null) {
+            b = buildBloomFilter();
+            bloomFilter = b;
+          }
+        }
+      }
+      return b == null || b.mightContain(key);
+    }
+
+    private BloomFilter<K> buildBloomFilter() {
+      SqlHandle c = null;
+      try {
+        c = acquire();
+        Statement s = c.conn.createStatement();
+        try {
+          ResultSet r;
+          if (estimatedSize <= 0) {
+            r = s.executeQuery("SELECT COUNT(*) FROM data");
+            try {
+              estimatedSize = r.next() ? r.getInt(1) : 0;
+            } finally {
+              r.close();
+            }
+          }
+
+          BloomFilter<K> b = newBloomFilter();
+          r = s.executeQuery("SELECT k FROM data");
+          try {
+            while (r.next()) {
+              b.put(keyType.get(r, 1));
+            }
+          } catch (JdbcSQLException e) {
+            if (e.getCause() instanceof InvalidClassException) {
+              log.warn("Entries cached for " + url
+                  + " have an incompatible class and can't be deserialized. "
+                  + "Cache is flushed.");
+              invalidateAll();
+            } else {
+              throw e;
+            }
+          } finally {
+            r.close();
+          }
+          return b;
+        } finally {
+          s.close();
+        }
+      } catch (SQLException e) {
+        log.warn("Cannot build BloomFilter for " + url, e);
+        c = close(c);
+        return null;
+      } finally {
+        release(c);
+      }
+    }
+
+    ValueHolder<V> getIfPresent(K key) {
+      SqlHandle c = null;
+      try {
+        c = acquire();
+        if (c.get == null) {
+          c.get = c.conn.prepareStatement("SELECT v FROM data WHERE k=?");
+        }
+        keyType.set(c.get, 1, key);
+        ResultSet r = c.get.executeQuery();
+        try {
+          if (!r.next()) {
+            missCount.incrementAndGet();
+            return null;
+          }
+
+          @SuppressWarnings("unchecked")
+          V val = (V) r.getObject(1);
+          ValueHolder<V> h = new ValueHolder<V>(val);
+          h.clean = true;
+          hitCount.incrementAndGet();
+          touch(c, key);
+          return h;
+        } finally {
+          r.close();
+          c.get.clearParameters();
+        }
+      } catch (SQLException e) {
+        log.warn("Cannot read cache " + url + " for " + key, e);
+        c = close(c);
+        return null;
+      } finally {
+        release(c);
+      }
+    }
+
+    private void touch(SqlHandle c, K key) throws SQLException {
+      if (c.touch == null) {
+        c.touch =c.conn.prepareStatement("UPDATE data SET accessed=? WHERE k=?");
+      }
+      try {
+        c.touch.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
+        keyType.set(c.touch, 2, key);
+        c.touch.executeUpdate();
+      } finally {
+        c.touch.clearParameters();
+      }
+    }
+
+    void put(K key, ValueHolder<V> holder) {
+      if (holder.clean) {
+        return;
+      }
+
+      BloomFilter<K> b = bloomFilter;
+      if (b != null) {
+        b.put(key);
+        bloomFilter = b;
+      }
+
+      SqlHandle c = null;
+      try {
+        c = acquire();
+        if (c.put == null) {
+          c.put = c.conn.prepareStatement("MERGE INTO data VALUES(?,?,?,?)");
+        }
+        try {
+          keyType.set(c.put, 1, key);
+          c.put.setObject(2, holder.value);
+          c.put.setTimestamp(3, new Timestamp(holder.created));
+          c.put.setTimestamp(4, new Timestamp(System.currentTimeMillis()));
+          c.put.executeUpdate();
+          holder.clean = true;
+        } finally {
+          c.put.clearParameters();
+        }
+      } catch (SQLException e) {
+        log.warn("Cannot put into cache " + url, e);
+        c = close(c);
+      } finally {
+        release(c);
+      }
+    }
+
+    void invalidate(K key) {
+      SqlHandle c = null;
+      try {
+        c = acquire();
+        invalidate(c, key);
+      } catch (SQLException e) {
+        log.warn("Cannot invalidate cache " + url, e);
+        c = close(c);
+      } finally {
+        release(c);
+      }
+    }
+
+    private void invalidate(SqlHandle c, K key) throws SQLException {
+      if (c.invalidate == null) {
+        c.invalidate = c.conn.prepareStatement("DELETE FROM data WHERE k=?");
+      }
+      try {
+        keyType.set(c.invalidate, 1, key);
+        c.invalidate.executeUpdate();
+      } finally {
+        c.invalidate.clearParameters();
+      }
+    }
+
+    void invalidateAll() {
+      SqlHandle c = null;
+      try {
+        c = acquire();
+        Statement s = c.conn.createStatement();
+        try {
+          s.executeUpdate("DELETE FROM data");
+        } finally {
+          s.close();
+        }
+        bloomFilter = newBloomFilter();
+      } catch (SQLException e) {
+        log.warn("Cannot invalidate cache " + url, e);
+        c = close(c);
+      } finally {
+        release(c);
+      }
+    }
+
+    void prune(Cache<K, ?> mem) {
+      SqlHandle c = null;
+      try {
+        c = acquire();
+        Statement s = c.conn.createStatement();
+        try {
+          long used = 0;
+          ResultSet r = s.executeQuery("SELECT"
+              + " SUM(OCTET_LENGTH(k) + OCTET_LENGTH(v))"
+              + " FROM data");
+          try {
+            used = r.next() ? r.getLong(1) : 0;
+          } finally {
+            r.close();
+          }
+          if (used <= maxSize) {
+            return;
+          }
+
+          r = s.executeQuery("SELECT"
+              + " k"
+              + ",OCTET_LENGTH(k) + OCTET_LENGTH(v)"
+              + " FROM data"
+              + " ORDER BY accessed");
+          try {
+            while (maxSize < used && r.next()) {
+              K key = keyType.get(r, 1);
+              if (mem.getIfPresent(key) != null) {
+                touch(c, key);
+              } else {
+                invalidate(c, key);
+                used -= r.getLong(2);
+              }
+            }
+          } finally {
+            r.close();
+          }
+        } finally {
+          s.close();
+        }
+      } catch (SQLException e) {
+        log.warn("Cannot prune cache " + url, e);
+        c = close(c);
+      } finally {
+        release(c);
+      }
+    }
+
+    DiskStats diskStats() {
+      DiskStats d = new DiskStats();
+      d.hitCount = hitCount.get();
+      d.missCount = missCount.get();
+      SqlHandle c = null;
+      try {
+        c = acquire();
+        Statement s = c.conn.createStatement();
+        try {
+          ResultSet r = s.executeQuery("SELECT"
+              + " COUNT(*)"
+              + ",SUM(OCTET_LENGTH(k) + OCTET_LENGTH(v))"
+              + " FROM data");
+          try {
+            if (r.next()) {
+              d.size = r.getLong(1);
+              d.space = r.getLong(2);
+            }
+          } finally {
+            r.close();
+          }
+        } finally {
+          s.close();
+        }
+      } catch (SQLException e) {
+        log.warn("Cannot get DiskStats for " + url, e);
+        c = close(c);
+      } finally {
+        release(c);
+      }
+      return d;
+    }
+
+    private SqlHandle acquire() throws SQLException {
+      SqlHandle h = handles.poll();
+      return h != null ? h : new SqlHandle(url, keyType);
+    }
+
+    private void release(SqlHandle h) {
+      if (h != null && !handles.offer(h)) {
+        h.close();
+      }
+    }
+
+    private SqlHandle close(SqlHandle h) {
+      if (h != null) {
+        h.close();
+      }
+      return null;
+    }
+
+    private BloomFilter<K> newBloomFilter() {
+      int cnt = Math.max(64 * 1024, 2 * estimatedSize);
+      return BloomFilter.create(keyType.funnel(), cnt);
+    }
+  }
+
+  static class SqlHandle {
+    private final String url;
+    Connection conn;
+    PreparedStatement get;
+    PreparedStatement put;
+    PreparedStatement touch;
+    PreparedStatement invalidate;
+
+    SqlHandle(String url, KeyType<?> type) throws SQLException {
+      this.url = url;
+      this.conn = org.h2.Driver.load().connect(url, null);
+      Statement stmt = conn.createStatement();
+      try {
+        stmt.execute("CREATE TABLE IF NOT EXISTS data"
+          + "(k " + type.columnType() + " NOT NULL PRIMARY KEY HASH"
+          + ",v OTHER NOT NULL"
+          + ",created TIMESTAMP NOT NULL"
+          + ",accessed TIMESTAMP NOT NULL"
+          + ")");
+      } finally {
+        stmt.close();
+      }
+    }
+
+    void close() {
+      get = closeStatement(get);
+      put = closeStatement(put);
+      touch = closeStatement(touch);
+      invalidate = closeStatement(invalidate);
+
+      if (conn != null) {
+        try {
+          conn.close();
+        } catch (SQLException e) {
+          log.warn("Cannot close connection to " + url, e);
+        } finally {
+          conn = null;
+        }
+      }
+    }
+
+    private PreparedStatement closeStatement(PreparedStatement ps) {
+      if (ps != null) {
+        try {
+          ps.close();
+        } catch (SQLException e) {
+          log.warn("Cannot close statement for " + url, e);
+        }
+      }
+      return null;
+    }
+  }
+
+  private static class SinkOutputStream extends OutputStream {
+    private final PrimitiveSink sink;
+
+    SinkOutputStream(PrimitiveSink sink) {
+      this.sink = sink;
+    }
+
+    @Override
+    public void write(int b) {
+      sink.putByte((byte)b);
+    }
+
+    @Override
+    public void write(byte[] b, int p, int n) {
+      sink.putBytes(b, p, n);
+    }
+  }
+}
diff --git a/gerrit-common/.gitignore b/gerrit-common/.gitignore
index 194bedc..759f12c 100644
--- a/gerrit-common/.gitignore
+++ b/gerrit-common/.gitignore
@@ -2,4 +2,5 @@
 /.classpath
 /.project
 /.settings/org.maven.ide.eclipse.prefs
-/.settings/org.eclipse.m2e.core.prefs
\ No newline at end of file
+/.settings/org.eclipse.m2e.core.prefs
+/gerrit-common.iml
\ No newline at end of file
diff --git a/gerrit-common/.settings/org.eclipse.core.resources.prefs b/gerrit-common/.settings/org.eclipse.core.resources.prefs
index fc11c3f..f9fe345 100644
--- a/gerrit-common/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-common/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:36 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding//src/test/java=UTF-8
diff --git a/gerrit-common/pom.xml b/gerrit-common/pom.xml
index e7933ea..9b3fe5f 100644
--- a/gerrit-common/pom.xml
+++ b/gerrit-common/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.4-SNAPSHOT</version>
+    <version>2.5-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-common</artifactId>
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
index 71df400..2b9b72a 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
@@ -14,13 +14,11 @@
 
 package com.google.gerrit.common;
 
-import com.google.gerrit.common.data.AccountInfo;
 import com.google.gerrit.common.data.ChangeInfo;
-import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Change.Status;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.Change.Status;
 import com.google.gwtorm.client.KeyUtil;
 
 public class PageLinks {
@@ -40,8 +38,10 @@
 
   public static final String MINE = "/";
   public static final String ADMIN_GROUPS = "/admin/groups/";
+  public static final String ADMIN_CREATE_GROUP = "/admin/create-group/";
   public static final String ADMIN_PROJECTS = "/admin/projects/";
   public static final String ADMIN_CREATE_PROJECT = "/admin/create-project/";
+  public static final String ADMIN_PLUGINS = "/admin/plugins/";
 
   public static String toChange(final ChangeInfo c) {
     return toChange(c.getId());
@@ -59,12 +59,12 @@
     return "/admin/projects/" + p.get() + ",access";
   }
 
-  public static String toAccountDashboard(final AccountInfo acct) {
-    return toAccountDashboard(acct.getId());
+  public static String toAccountQuery(final String fullname) {
+    return toAccountQuery(fullname, Status.NEW);
   }
 
-  public static String toAccountDashboard(final Account.Id acct) {
-    return "/dashboard/" + acct.toString();
+  public static String toAccountQuery(String fullname, Status status) {
+    return toChangeQuery(op("owner", fullname) + " " + status(status), TOP);
   }
 
   public static String toChangeQuery(final String query) {
@@ -72,30 +72,38 @@
   }
 
   public static String toChangeQuery(String query, String page) {
-    query = KeyUtil.encode(query).replaceAll("%3[Aa]", ":");
-    return "/q/" + query + "," + page;
+    return "/q/" + KeyUtil.encode(query) + "," + page;
   }
 
   public static String projectQuery(Project.NameKey proj, Status status) {
+      return status(status) + " " + op("project", proj.get());
+  }
+
+  private static String status(Status status) {
     switch (status) {
       case ABANDONED:
-        return "status:abandoned " + op("project", proj.get());
-
+        return "status:abandoned";
       case MERGED:
-        return "status:merged " + op("project", proj.get());
-
+        return "status:merged";
       case NEW:
       case SUBMITTED:
       default:
-        return "status:open " + op("project", proj.get());
+        return "status:open";
     }
   }
 
-  public static String op(String name, String value) {
-    if (value.indexOf(' ') >= 0) {
-      return name + ":\"" + value + "\"";
+  public static String op(String op, String value) {
+    if (isSingleWord(value)) {
+      return op + ":" + value;
     }
-    return name + ":" + value;
+    return op + ":\"" + value + "\"";
+  }
+
+  private static boolean isSingleWord(String value) {
+    if (value.startsWith("-")) {
+      return false;
+    }
+    return value.matches("[^\u0000-\u0020!\"#$%&'():;?\\[\\]{}~]+");
   }
 
   protected PageLinks() {
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/audit/Audit.java b/gerrit-common/src/main/java/com/google/gerrit/common/audit/Audit.java
new file mode 100644
index 0000000..90c7f75
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/audit/Audit.java
@@ -0,0 +1,36 @@
+// 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.common.audit;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Audit annotation for JSON/RPC interfaces.
+ *
+ * Flag with @Audit all the JSON/RPC methods to
+ * be traced in audit-trail and submitted to the
+ * AuditService.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Audit {
+  String action() default "";
+
+  /** List of positions of parameters to be obfuscated in audit-trail (i.e. passwords) */
+  int[] obfuscate() default {};
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/auth/userpass/UserPassAuthService.java b/gerrit-common/src/main/java/com/google/gerrit/common/auth/userpass/UserPassAuthService.java
index 1d25a3d..0936d23 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/auth/userpass/UserPassAuthService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/auth/userpass/UserPassAuthService.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.common.auth.userpass;
 
+import com.google.gerrit.common.audit.Audit;
 import com.google.gwtjsonrpc.common.AsyncCallback;
 import com.google.gwtjsonrpc.common.AllowCrossSiteRequest;
 import com.google.gwtjsonrpc.common.RemoteJsonService;
@@ -22,6 +23,7 @@
 
 @RpcImpl(version = Version.V2_0)
 public interface UserPassAuthService extends RemoteJsonService {
+  @Audit(action = "sign in", obfuscate={1})
   @AllowCrossSiteRequest
   void authenticate(String username, String password,
       AsyncCallback<LoginResult> callback);
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java b/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java
new file mode 100644
index 0000000..a5ab851
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java
@@ -0,0 +1,73 @@
+// 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.common.changes;
+
+import java.util.EnumSet;
+
+/** Output options available when using {@code /changes/} RPCs. */
+public enum ListChangesOption {
+  LABELS(0),
+
+  /** Return information on the current patch set of the change. */
+  CURRENT_REVISION(1),
+  ALL_REVISIONS(2),
+
+  /** If revisions are included, parse the commit object. */
+  CURRENT_COMMIT(3),
+  ALL_COMMITS(4),
+
+  /** If a patch set is included, include the files of the patch set. */
+  CURRENT_FILES(5),
+  ALL_FILES(6);
+
+  private final int value;
+
+  private ListChangesOption(int v) {
+    this.value = v;
+  }
+
+  public int getValue() {
+    return value;
+  }
+
+  public static ListChangesOption fromValue(int value) {
+    return ListChangesOption.values()[value];
+  }
+
+  public static EnumSet<ListChangesOption> fromBits(int v) {
+    EnumSet<ListChangesOption> r = EnumSet.noneOf(ListChangesOption.class);
+    for (ListChangesOption o : ListChangesOption.values()) {
+      if ((v & (1 << o.value)) != 0) {
+        r.add(o);
+        v &= ~(1 << o.value);
+      }
+      if (v == 0) {
+        return r;
+      }
+    }
+    if (v != 0) {
+      throw new IllegalArgumentException("unknown " + Integer.toHexString(v));
+    }
+    return r;
+  }
+
+  public static int toBits(EnumSet<ListChangesOption> set) {
+    int r = 0;
+    for (ListChangesOption o : set) {
+      r |= 1 << o.value;
+    }
+    return r;
+  }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccessSection.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccessSection.java
index cd64b0a..a9b5e85 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccessSection.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccessSection.java
@@ -118,4 +118,13 @@
   public String toString() {
     return "AccessSection[" + getName() + "]";
   }
+
+  @Override
+  public boolean equals(final Object obj) {
+    if (!super.equals(obj) || !(obj instanceof AccessSection)) {
+      return false;
+    }
+    return new HashSet<Permission>(permissions).equals(new HashSet<Permission>(
+        ((AccessSection) obj).permissions));
+  }
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountInfo.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountInfo.java
index 9a4d9fb..92c2d6c 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountInfo.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountInfo.java
@@ -65,4 +65,51 @@
   public void setPreferredEmail(final String email) {
     preferredEmail = email;
   }
+
+  /**
+   * Formats an account name.
+   * <p>
+   * If the account has a full name, it returns only the full name. Otherwise it
+   * returns a longer form that includes the email address.
+   */
+  public String getName(String anonymousCowardName) {
+    if (getFullName() != null) {
+      return getFullName();
+    }
+    if (getPreferredEmail() != null) {
+      return getPreferredEmail();
+    }
+    return getNameEmail(anonymousCowardName);
+  }
+
+  /**
+   * Formats an account as an name and an email address.
+   * <p>
+   * Example output:
+   * <ul>
+   * <li><code>A U. Thor &lt;author@example.com&gt;</code>: full populated</li>
+   * <li><code>A U. Thor (12)</code>: missing email address</li>
+   * <li><code>Anonymous Coward &lt;author@example.com&gt;</code>: missing name</li>
+   * <li><code>Anonymous Coward (12)</code>: missing name and email address</li>
+   * </ul>
+   */
+  public String getNameEmail(String anonymousCowardName) {
+    String name = getFullName();
+    if (name == null) {
+      name = anonymousCowardName;
+    }
+
+    final StringBuilder b = new StringBuilder();
+    b.append(name);
+    if (getPreferredEmail() != null) {
+      b.append(" <");
+      b.append(getPreferredEmail());
+      b.append(">");
+    } else if (getId() != null) {
+      b.append(" (");
+      b.append(getId().get());
+      b.append(")");
+    }
+    return b.toString();
+  }
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java
index 21aca69..aa212f9 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java
@@ -14,17 +14,18 @@
 
 package com.google.gerrit.common.data;
 
+import com.google.gerrit.common.audit.Audit;
 import com.google.gerrit.common.auth.SignInRequired;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
+import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.AccountSshKey;
 import com.google.gerrit.reviewdb.client.ContactInformation;
-import com.google.gerrit.reviewdb.client.ContributorAgreement;
 import com.google.gwtjsonrpc.common.AsyncCallback;
 import com.google.gwtjsonrpc.common.RemoteJsonService;
 import com.google.gwtjsonrpc.common.RpcImpl;
-import com.google.gwtjsonrpc.common.VoidResult;
 import com.google.gwtjsonrpc.common.RpcImpl.Version;
+import com.google.gwtjsonrpc.common.VoidResult;
 
 import java.util.List;
 import java.util.Set;
@@ -34,20 +35,25 @@
   @SignInRequired
   void mySshKeys(AsyncCallback<List<AccountSshKey>> callback);
 
+  @Audit
   @SignInRequired
   void addSshKey(String keyText, AsyncCallback<AccountSshKey> callback);
 
+  @Audit
   @SignInRequired
   void deleteSshKeys(Set<AccountSshKey.Id> ids,
       AsyncCallback<VoidResult> callback);
 
+  @Audit
   @SignInRequired
   void changeUserName(String newName, AsyncCallback<VoidResult> callback);
 
+  @Audit
   @SignInRequired
   void generatePassword(AccountExternalId.Key key,
       AsyncCallback<AccountExternalId> callback);
 
+  @Audit
   @SignInRequired
   void clearPassword(AccountExternalId.Key key,
       AsyncCallback<AccountExternalId> gerritCallback);
@@ -56,23 +62,28 @@
   void myExternalIds(AsyncCallback<List<AccountExternalId>> callback);
 
   @SignInRequired
-  void myGroups(AsyncCallback<List<GroupDetail>> callback);
+  void myGroups(AsyncCallback<List<AccountGroup>> callback);
 
+  @Audit
   @SignInRequired
   void deleteExternalIds(Set<AccountExternalId.Key> keys,
       AsyncCallback<Set<AccountExternalId.Key>> callback);
 
+  @Audit
   @SignInRequired
   void updateContact(String fullName, String emailAddr,
       ContactInformation info, AsyncCallback<Account> callback);
 
+  @Audit
   @SignInRequired
-  void enterAgreement(ContributorAgreement.Id id,
+  void enterAgreement(String agreementName,
       AsyncCallback<VoidResult> callback);
 
+  @Audit
   @SignInRequired
   void registerEmail(String address, AsyncCallback<Account> callback);
 
+  @Audit
   @SignInRequired
   void validateEmail(String token, AsyncCallback<VoidResult> callback);
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountService.java
index 7377d7e..18cf657 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountService.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.common.data;
 
+import com.google.gerrit.common.audit.Audit;
 import com.google.gerrit.common.auth.SignInRequired;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountDiffPreference;
@@ -36,10 +37,12 @@
   @SignInRequired
   void myDiffPreferences(AsyncCallback<AccountDiffPreference> callback);
 
+  @Audit
   @SignInRequired
   void changePreferences(AccountGeneralPreferences pref,
       AsyncCallback<VoidResult> gerritCallback);
 
+  @Audit
   @SignInRequired
   void changeDiffPreferences(AccountDiffPreference diffPref,
       AsyncCallback<VoidResult> callback);
@@ -47,14 +50,17 @@
   @SignInRequired
   void myProjectWatch(AsyncCallback<List<AccountProjectWatchInfo>> callback);
 
+  @Audit
   @SignInRequired
   void addProjectWatch(String projectName, String filter,
       AsyncCallback<AccountProjectWatchInfo> callback);
 
+  @Audit
   @SignInRequired
   void updateProjectWatch(AccountProjectWatch watch,
       AsyncCallback<VoidResult> callback);
 
+  @Audit
   @SignInRequired
   void deleteProjectWatches(Set<AccountProjectWatch.Key> keys,
       AsyncCallback<VoidResult> callback);
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AgreementInfo.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AgreementInfo.java
index 0c6f6b7..7464bd1 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/AgreementInfo.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AgreementInfo.java
@@ -14,30 +14,21 @@
 
 package com.google.gerrit.common.data;
 
-import com.google.gerrit.reviewdb.client.AccountAgreement;
-import com.google.gerrit.reviewdb.client.AccountGroupAgreement;
-import com.google.gerrit.reviewdb.client.ContributorAgreement;
-
 import java.util.List;
 import java.util.Map;
 
 public class AgreementInfo {
-  public List<AccountAgreement> userAccepted;
-  public List<AccountGroupAgreement> groupAccepted;
-  public Map<ContributorAgreement.Id, ContributorAgreement> agreements;
+  public List<String> accepted;
+  public Map<String, ContributorAgreement> agreements;
 
   public AgreementInfo() {
   }
 
-  public void setUserAccepted(List<AccountAgreement> a) {
-    userAccepted = a;
+  public void setAccepted(List<String> a) {
+    accepted = a;
   }
 
-  public void setGroupAccepted(List<AccountGroupAgreement> a) {
-    groupAccepted = a;
-  }
-
-  public void setAgreements(Map<ContributorAgreement.Id, ContributorAgreement> a) {
+  public void setAgreements(Map<String, ContributorAgreement> a) {
     agreements = a;
   }
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java
index 8b43624..c50d2e3 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.common.data;
 
+import com.google.gerrit.common.audit.Audit;
 import com.google.gerrit.common.auth.SignInRequired;
 import com.google.gerrit.reviewdb.client.AccountDiffPreference;
 import com.google.gerrit.reviewdb.client.Change;
@@ -25,12 +26,16 @@
 
 @RpcImpl(version = Version.V2_0)
 public interface ChangeDetailService extends RemoteJsonService {
+  @Audit
   void changeDetail(Change.Id id, AsyncCallback<ChangeDetail> callback);
 
+  @Audit
   void includedInDetail(Change.Id id, AsyncCallback<IncludedInDetail> callback);
 
+  @Audit
   void patchSetDetail(PatchSet.Id key, AsyncCallback<PatchSetDetail> callback);
 
+  @Audit
   void patchSetDetail2(PatchSet.Id baseId, PatchSet.Id key,
       AccountDiffPreference diffPrefs, AsyncCallback<PatchSetDetail> callback);
 
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java
index f646bc6..0c466497 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java
@@ -14,39 +14,22 @@
 
 package com.google.gerrit.common.data;
 
+import com.google.gerrit.common.audit.Audit;
 import com.google.gerrit.common.auth.SignInRequired;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
 import com.google.gwtjsonrpc.common.AsyncCallback;
 import com.google.gwtjsonrpc.common.RemoteJsonService;
 import com.google.gwtjsonrpc.common.RpcImpl;
 import com.google.gwtjsonrpc.common.VoidResult;
 import com.google.gwtjsonrpc.common.RpcImpl.Version;
 
-import java.util.Set;
-
 @RpcImpl(version = Version.V2_0)
 public interface ChangeListService extends RemoteJsonService {
-  /** Get all changes which match an arbitrary query string. */
-  void allQueryPrev(String query, String pos, int limit,
-      AsyncCallback<SingleListChangeInfo> callback);
-
-  /** Get all changes which match an arbitrary query string. */
-  void allQueryNext(String query, String pos, int limit,
-      AsyncCallback<SingleListChangeInfo> callback);
-
-  /** Get the data to show AccountDashboardScreen for an account. */
-  void forAccount(Account.Id id, AsyncCallback<AccountDashboardInfo> callback);
-
-  /** Get the ids of all changes starred by the caller. */
-  @SignInRequired
-  void myStarredChangeIds(AsyncCallback<Set<Change.Id>> callback);
-
   /**
    * Add and/or remove changes from the set of starred changes of the caller.
    *
    * @param req the add and remove cluster.
    */
+  @Audit
   @SignInRequired
   void toggleStars(ToggleStarRequest req, AsyncCallback<VoidResult> callback);
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeManageService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeManageService.java
index 872bddc..4ef6b3e 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeManageService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeManageService.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.common.data;
 
+import com.google.gerrit.common.audit.Audit;
 import com.google.gerrit.common.auth.SignInRequired;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gwtjsonrpc.common.AsyncCallback;
@@ -24,27 +25,34 @@
 
 @RpcImpl(version = Version.V2_0)
 public interface ChangeManageService extends RemoteJsonService {
+  @Audit
   @SignInRequired
   void submit(PatchSet.Id patchSetId, AsyncCallback<ChangeDetail> callback);
 
+  @Audit
   @SignInRequired
   void abandonChange(PatchSet.Id patchSetId, String message,
       AsyncCallback<ChangeDetail> callback);
 
+  @Audit
   @SignInRequired
   void revertChange(PatchSet.Id patchSetId, String message,
       AsyncCallback<ChangeDetail> callback);
 
+  @Audit
   @SignInRequired
   void restoreChange(PatchSet.Id patchSetId, String message,
       AsyncCallback<ChangeDetail> callback);
 
+  @Audit
   @SignInRequired
   void publish(PatchSet.Id patchSetId, AsyncCallback<ChangeDetail> callback);
 
+  @Audit
   @SignInRequired
   void deleteDraftChange(PatchSet.Id patchSetId, AsyncCallback<VoidResult> callback);
 
+  @Audit
   @SignInRequired
   void rebaseChange(PatchSet.Id patchSetId, AsyncCallback<ChangeDetail> callback);
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ContributorAgreement.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ContributorAgreement.java
new file mode 100644
index 0000000..e02d9d3
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ContributorAgreement.java
@@ -0,0 +1,111 @@
+// 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.common.data;
+
+import com.google.gerrit.reviewdb.client.Project;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Portion of a {@link Project} describing a single contributor agreement. */
+public class ContributorAgreement implements Comparable<ContributorAgreement> {
+  protected String name;
+  protected String description;
+  protected List<PermissionRule> accepted;
+  protected boolean requireContactInformation;
+  protected GroupReference autoVerify;
+  protected String agreementUrl;
+
+  protected ContributorAgreement() {
+  }
+
+  public ContributorAgreement(String name) {
+    setName(name);
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public void setDescription(String description) {
+    this.description = description;
+  }
+
+  public List<PermissionRule> getAccepted() {
+    if (accepted == null) {
+      accepted = new ArrayList<PermissionRule>();
+    }
+    return accepted;
+  }
+
+  public void setAccepted(List<PermissionRule> accepted) {
+    this.accepted = accepted;
+  }
+
+  public boolean isRequireContactInformation() {
+    return requireContactInformation;
+  }
+
+  public void setRequireContactInformation(boolean requireContactInformation) {
+    this.requireContactInformation = requireContactInformation;
+  }
+
+  public GroupReference getAutoVerify() {
+    return autoVerify;
+  }
+
+  public void setAutoVerify(GroupReference autoVerify) {
+    this.autoVerify = autoVerify;
+  }
+
+  public String getAgreementUrl() {
+    return agreementUrl;
+  }
+
+  public void setAgreementUrl(String agreementUrl) {
+    this.agreementUrl = agreementUrl;
+  }
+
+  @Override
+  public int compareTo(ContributorAgreement o) {
+    return getName().compareTo(o.getName());
+  }
+
+  @Override
+  public String toString() {
+    return "ContributorAgreement[" + getName() + "]";
+  }
+
+  public ContributorAgreement forUi() {
+    ContributorAgreement ca = new ContributorAgreement(name);
+    ca.description = description;
+    ca.accepted = Collections.emptyList();
+    ca.requireContactInformation = requireContactInformation;
+    if (autoVerify != null) {
+      ca.autoVerify = new GroupReference();
+    }
+    ca.agreementUrl = agreementUrl ;
+    return ca;
+  }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
index 456ffb4..7c16129 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
@@ -16,9 +16,11 @@
 
 import com.google.gerrit.common.auth.openid.OpenIdProviderPattern;
 import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Account.FieldName;
+import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand;
+import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadScheme;
 import com.google.gerrit.reviewdb.client.AuthType;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadScheme;
 import com.google.gwtexpui.safehtml.client.RegexFindReplace;
 
 import java.util.List;
@@ -27,6 +29,8 @@
 public class GerritConfig implements Cloneable {
   protected String registerUrl;
   protected String httpPasswordUrl;
+  protected String reportBugUrl;
+  protected String openIdSsoUrl;
   protected List<OpenIdProviderPattern> allowedOpenIDs;
 
   protected GitwebConfig gitweb;
@@ -35,9 +39,11 @@
   protected boolean allowRegisterNewEmail;
   protected AuthType authType;
   protected Set<DownloadScheme> downloadSchemes;
+  protected Set<DownloadCommand> downloadCommands;
   protected String gitDaemonUrl;
   protected String gitHttpUrl;
   protected String sshdAddress;
+  protected String editFullNameUrl;
   protected Project.NameKey wildProject;
   protected ApprovalTypes approvalTypes;
   protected Set<Account.FieldName> editableAccountFields;
@@ -54,6 +60,22 @@
     registerUrl = u;
   }
 
+  public String getReportBugUrl() {
+    return reportBugUrl;
+  }
+
+  public void setReportBugUrl(String u) {
+    reportBugUrl = u;
+  }
+
+  public String getEditFullNameUrl() {
+    return editFullNameUrl;
+  }
+
+  public void setEditFullNameUrl(String u) {
+    editFullNameUrl = u;
+  }
+
   public String getHttpPasswordUrl() {
     return httpPasswordUrl;
   }
@@ -62,6 +84,14 @@
     httpPasswordUrl = url;
   }
 
+  public String getOpenIdSsoUrl() {
+      return openIdSsoUrl;
+  }
+
+  public void setOpenIdSsoUrl(final String u) {
+    openIdSsoUrl = u;
+  }
+
   public List<OpenIdProviderPattern> getAllowedOpenIDs() {
     return allowedOpenIDs;
   }
@@ -86,6 +116,14 @@
     downloadSchemes = s;
   }
 
+  public Set<DownloadCommand> getDownloadCommands() {
+    return downloadCommands;
+  }
+
+  public void setDownloadCommands(final Set<DownloadCommand> downloadCommands) {
+    this.downloadCommands = downloadCommands;
+  }
+
   public GitwebConfig getGitwebLink() {
     return gitweb;
   }
@@ -199,4 +237,13 @@
   public void setAnonymousCowardName(final String anonymousCowardName) {
     this.anonymousCowardName = anonymousCowardName;
   }
+
+  public boolean siteHasUsernames() {
+    if (getAuthType() == AuthType.CUSTOM_EXTENSION
+        && getHttpPasswordUrl() != null
+        && !canEdit(FieldName.USER_NAME)) {
+      return false;
+    }
+    return true;
+  }
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
index d3d2a4d..81d4fc9 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
@@ -15,6 +15,8 @@
 package com.google.gerrit.common.data;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
 /** Server wide capabilities. Represented as {@link Permission} objects. */
@@ -40,12 +42,13 @@
   public static final String CREATE_PROJECT = "createProject";
 
   /**
-   * Denotes who may email change reviewers.
+   * Denotes who may email change reviewers and watchers.
    * <p>
    * This can be used to deny build bots from emailing reviewers and people who
-   * have starred the changed. Instead, only the authors of the change will be
-   * emailed. The allow rules are evaluated before deny rules, however the
-   * default is to allow emailing, if no explicit rule is matched.
+   * watch the change. Instead, only the authors of the change and those who
+   * starred it will be emailed. The allow rules are evaluated before deny
+   * rules, however the default is to allow emailing, if no explicit rule is
+   * matched.
    */
   public static final String EMAIL_REVIEWERS = "emailReviewers";
 
@@ -73,23 +76,34 @@
   /** Can view all pending tasks in the queue (not just the filtered set). */
   public static final String VIEW_QUEUE = "viewQueue";
 
+  private static final List<String> NAMES_ALL;
   private static final List<String> NAMES_LC;
 
   static {
-    NAMES_LC = new ArrayList<String>();
-    NAMES_LC.add(ADMINISTRATE_SERVER.toLowerCase());
-    NAMES_LC.add(CREATE_ACCOUNT.toLowerCase());
-    NAMES_LC.add(CREATE_GROUP.toLowerCase());
-    NAMES_LC.add(CREATE_PROJECT.toLowerCase());
-    NAMES_LC.add(EMAIL_REVIEWERS.toLowerCase());
-    NAMES_LC.add(FLUSH_CACHES.toLowerCase());
-    NAMES_LC.add(KILL_TASK.toLowerCase());
-    NAMES_LC.add(PRIORITY.toLowerCase());
-    NAMES_LC.add(QUERY_LIMIT.toLowerCase());
-    NAMES_LC.add(START_REPLICATION.toLowerCase());
-    NAMES_LC.add(VIEW_CACHES.toLowerCase());
-    NAMES_LC.add(VIEW_CONNECTIONS.toLowerCase());
-    NAMES_LC.add(VIEW_QUEUE.toLowerCase());
+    NAMES_ALL = new ArrayList<String>();
+    NAMES_ALL.add(ADMINISTRATE_SERVER);
+    NAMES_ALL.add(CREATE_ACCOUNT);
+    NAMES_ALL.add(CREATE_GROUP);
+    NAMES_ALL.add(CREATE_PROJECT);
+    NAMES_ALL.add(EMAIL_REVIEWERS);
+    NAMES_ALL.add(FLUSH_CACHES);
+    NAMES_ALL.add(KILL_TASK);
+    NAMES_ALL.add(PRIORITY);
+    NAMES_ALL.add(QUERY_LIMIT);
+    NAMES_ALL.add(START_REPLICATION);
+    NAMES_ALL.add(VIEW_CACHES);
+    NAMES_ALL.add(VIEW_CONNECTIONS);
+    NAMES_ALL.add(VIEW_QUEUE);
+
+    NAMES_LC = new ArrayList<String>(NAMES_ALL.size());
+    for (String name : NAMES_ALL) {
+      NAMES_LC.add(name.toLowerCase());
+    }
+  }
+
+  /** @return all valid capability names. */
+  public static Collection<String> getAllNames() {
+    return Collections.unmodifiableList(NAMES_ALL);
   }
 
   /** @return true if the name is recognized as a capability name. */
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
index f385e27..5cb7fa2 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.common.data;
 
+import com.google.gerrit.common.audit.Audit;
 import com.google.gerrit.common.auth.SignInRequired;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.AccountGroupInclude;
@@ -24,61 +25,64 @@
 import com.google.gwtjsonrpc.common.VoidResult;
 import com.google.gwtjsonrpc.common.RpcImpl.Version;
 
-import java.util.List;
 import java.util.Set;
 
 @RpcImpl(version = Version.V2_0)
 public interface GroupAdminService extends RemoteJsonService {
+  @Audit
   @SignInRequired
   void visibleGroups(AsyncCallback<GroupList> callback);
 
+  @Audit
   @SignInRequired
   void createGroup(String newName, AsyncCallback<AccountGroup.Id> callback);
 
+  @Audit
   @SignInRequired
   void groupDetail(AccountGroup.Id groupId, AccountGroup.UUID uuid,
       AsyncCallback<GroupDetail> callback);
 
+  @Audit
   @SignInRequired
   void changeGroupDescription(AccountGroup.Id groupId, String description,
       AsyncCallback<VoidResult> callback);
 
+  @Audit
   @SignInRequired
   void changeGroupOptions(AccountGroup.Id groupId, GroupOptions groupOptions,
       AsyncCallback<VoidResult> callback);
 
+  @Audit
   @SignInRequired
   void changeGroupOwner(AccountGroup.Id groupId, String newOwnerName,
       AsyncCallback<VoidResult> callback);
 
+  @Audit
   @SignInRequired
   void renameGroup(AccountGroup.Id groupId, String newName,
       AsyncCallback<GroupDetail> callback);
 
+  @Audit
   @SignInRequired
   void changeGroupType(AccountGroup.Id groupId, AccountGroup.Type newType,
       AsyncCallback<VoidResult> callback);
 
-  @SignInRequired
-  void changeExternalGroup(AccountGroup.Id groupId,
-      AccountGroup.ExternalNameKey bindTo, AsyncCallback<VoidResult> callback);
-
-  @SignInRequired
-  void searchExternalGroups(String searchFilter,
-      AsyncCallback<List<AccountGroup.ExternalNameKey>> callback);
-
+  @Audit
   @SignInRequired
   void addGroupMember(AccountGroup.Id groupId, String nameOrEmail,
       AsyncCallback<GroupDetail> callback);
 
+  @Audit
   @SignInRequired
   void addGroupInclude(AccountGroup.Id groupId, String groupName,
       AsyncCallback<GroupDetail> callback);
 
+  @Audit
   @SignInRequired
   void deleteGroupMembers(AccountGroup.Id groupId,
       Set<AccountGroupMember.Key> keys, AsyncCallback<VoidResult> callback);
 
+  @Audit
   @SignInRequired
   void deleteGroupIncludes(AccountGroup.Id groupId,
       Set<AccountGroupInclude.Key> keys, AsyncCallback<VoidResult> callback);
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDescription.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDescription.java
new file mode 100644
index 0000000..828bf24
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDescription.java
@@ -0,0 +1,48 @@
+// 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.common.data;
+
+import com.google.gerrit.reviewdb.client.AccountGroup;
+
+/**
+ * Group methods exposed by the GroupBackend.
+ */
+public class GroupDescription {
+  /**
+   * The Basic information required to be exposed by any Group.
+   */
+  public interface Basic {
+    /** @return the non-null UUID of the group. */
+    AccountGroup.UUID getGroupUUID();
+
+    /** @return the non-null name of the group. */
+    String getName();
+
+    /** @return whether the group is visible to all accounts. */
+    boolean isVisibleToAll();
+  }
+
+  /**
+   * The extended information exposed by internal groups backed by an
+   * AccountGroup.
+   */
+  public interface Internal extends Basic {
+    /** @return the backing AccountGroup. */
+    AccountGroup getAccountGroup();
+  }
+
+  private GroupDescription() {
+  }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDescriptions.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDescriptions.java
new file mode 100644
index 0000000..e0bc7d8
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDescriptions.java
@@ -0,0 +1,60 @@
+// 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.common.data;
+
+import com.google.gerrit.reviewdb.client.AccountGroup;
+
+import javax.annotation.Nullable;
+
+/**
+ * Utility class for building GroupDescription objects.
+ */
+public class GroupDescriptions {
+
+  @Nullable
+  public static AccountGroup toAccountGroup(GroupDescription.Basic group) {
+    if (group instanceof GroupDescription.Internal) {
+      return ((GroupDescription.Internal) group).getAccountGroup();
+    }
+    return null;
+  }
+
+  public static GroupDescription.Internal forAccountGroup(final AccountGroup group) {
+    return new GroupDescription.Internal() {
+      @Override
+      public AccountGroup.UUID getGroupUUID() {
+        return group.getGroupUUID();
+      }
+
+      @Override
+      public String getName() {
+        return group.getName();
+      }
+
+      @Override
+      public boolean isVisibleToAll() {
+        return group.isVisibleToAll();
+      }
+
+      @Override
+      public AccountGroup getAccountGroup() {
+        return group;
+      }
+    };
+  }
+
+  private GroupDescriptions() {
+  }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java
index 65723f7..01c7985 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java
@@ -26,7 +26,7 @@
   public AccountGroup group;
   public List<AccountGroupMember> members;
   public List<AccountGroupInclude> includes;
-  public AccountGroup ownerGroup;
+  public GroupReference ownerGroup;
   public boolean canModify;
 
   public GroupDetail() {
@@ -52,7 +52,7 @@
     includes = i;
   }
 
-  public void setOwnerGroup(AccountGroup g) {
+  public void setOwnerGroup(GroupReference g) {
     ownerGroup = g;
   }
 
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupList.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupList.java
index 6352461..b3095cd 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupList.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupList.java
@@ -14,25 +14,27 @@
 
 package com.google.gerrit.common.data;
 
+import com.google.gerrit.reviewdb.client.AccountGroup;
+
 import java.util.List;
 
 public class GroupList {
-  protected List<GroupDetail> groups;
+  protected List<AccountGroup> groups;
   protected boolean canCreateGroup;
 
   protected GroupList() {
   }
 
-  public GroupList(final List<GroupDetail> groups, final boolean canCreateGroup) {
+  public GroupList(final List<AccountGroup> groups, final boolean canCreateGroup) {
     this.groups = groups;
     this.canCreateGroup = canCreateGroup;
   }
 
-  public List<GroupDetail> getGroups() {
+  public List<AccountGroup> getGroups() {
     return groups;
   }
 
-  public void setGroups(List<GroupDetail> groups) {
+  public void setGroups(List<AccountGroup> groups) {
     this.groups = groups;
   }
 
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
index f05d1b9..c261fdd 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
@@ -23,6 +23,10 @@
     return new GroupReference(group.getGroupUUID(), group.getName());
   }
 
+  public static GroupReference forGroup(GroupDescription.Basic group) {
+    return new GroupReference(group.getGroupUUID(), group.getName());
+  }
+
   protected String uuid;
   protected String name;
 
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
index c3d3f1e..f991f4c 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
@@ -31,5 +31,8 @@
     public String textColor;
     public String trimColor;
     public String selectionColor;
+    public String changeTableOutdatedColor;
+    public String tableOddRowColor;
+    public String tableEvenRowColor;
   }
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ParameterizedString.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ParameterizedString.java
index 68676cf..2a70d6c 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ParameterizedString.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ParameterizedString.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.common.data;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -62,28 +63,9 @@
       raw.append(pattern.substring(i, b));
       ops.add(new Constant(pattern.substring(i, b)));
 
-      String expr = pattern.substring(b + 2, e);
-      String parameterName = "";
-      List<Function> functions = new ArrayList<Function>();
-      if (!expr.contains(".")) {
-        parameterName = expr;
-      } else {
-        int firstDot = expr.indexOf('.');
-        parameterName = expr.substring(0, firstDot);
-        String actionsStr = expr.substring(firstDot + 1);
-        String[] actions = actionsStr.split("\\.");
+      // "${parameter[.functions...]}" -> "parameter[.functions...]"
+      final Parameter p = new Parameter(pattern.substring(b + 2, e));
 
-        for (String action : actions) {
-          Function function = FUNCTIONS.get(action);
-          if (function == null) {
-            function = NOOP;
-          }
-          functions.add(function);
-        }
-      }
-
-      final Parameter p =
-          new Parameter(parameterName, Collections.unmodifiableList(functions));
       raw.append("{" + prs.size() + "}");
       prs.add(p);
       ops.add(p);
@@ -184,9 +166,25 @@
     private final String name;
     private final List<Function> functions;
 
-    Parameter(final String name, final List<Function> functions) {
-      this.name = name;
-      this.functions = functions;
+    Parameter(final String parameter) {
+      // "parameter[.functions...]" -> (parameter, functions...)
+      final List<String> names = Arrays.asList(parameter.split("\\."));
+      final List<Function> functs = new ArrayList<Function>(names.size());
+
+      if (names.isEmpty()) {
+        name = "";
+      } else {
+        name = names.get(0);
+
+        for (String fname : names.subList(1, names.size())) {
+          final Function function = FUNCTIONS.get(fname);
+          if (function != null) {
+            functs.add(function);
+          }
+        }
+      }
+
+      functions = Collections.unmodifiableList(functs);
     }
 
     @Override
@@ -207,12 +205,6 @@
   }
 
   private static final Map<String, Function> FUNCTIONS = initFunctions();
-  private static final Function NOOP = new Function() {
-    @Override
-    String apply(String a) {
-      return a;
-    }
-  };
 
   private static Map<String, Function> initFunctions() {
     final HashMap<String, Function> m = new HashMap<String, Function>();
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchDetailService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchDetailService.java
index 0191544..91ecb92 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchDetailService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchDetailService.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.common.data;
 
+import com.google.gerrit.common.audit.Audit;
 import com.google.gerrit.common.auth.SignInRequired;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountDiffPreference;
@@ -34,13 +35,16 @@
 
 @RpcImpl(version = Version.V2_0)
 public interface PatchDetailService extends RemoteJsonService {
+  @Audit
   void patchScript(Patch.Key key, PatchSet.Id a, PatchSet.Id b,
       AccountDiffPreference diffPrefs, AsyncCallback<PatchScript> callback);
 
+  @Audit
   @SignInRequired
   void saveDraft(PatchLineComment comment,
       AsyncCallback<PatchLineComment> callback);
 
+  @Audit
   @SignInRequired
   void deleteDraft(PatchLineComment.Key key, AsyncCallback<VoidResult> callback);
 
@@ -57,18 +61,22 @@
    *        change, then <code>null</code> is passed as result to
    *        {@link AsyncCallback#onSuccess(Object)}
    */
+  @Audit
   @SignInRequired
   void deleteDraftPatchSet(PatchSet.Id psid, AsyncCallback<ChangeDetail> callback);
 
+  @Audit
   @SignInRequired
   void publishComments(PatchSet.Id psid, String message,
       Set<ApprovalCategoryValue.Id> approvals,
       AsyncCallback<VoidResult> callback);
 
+  @Audit
   @SignInRequired
   void addReviewers(Change.Id id, List<String> reviewers, boolean confirmed,
       AsyncCallback<ReviewerResult> callback);
 
+  @Audit
   @SignInRequired
   void removeReviewer(Change.Id id, Account.Id reviewerId,
       AsyncCallback<ReviewerResult> callback);
@@ -82,6 +90,7 @@
   /**
    * Update the reviewed status for the patch.
    */
+  @Audit
   @SignInRequired
   void setReviewedByCurrentUser(Key patchKey, boolean reviewed, AsyncCallback<VoidResult> callback);
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java
index 075d558..3c5c688 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java
@@ -20,6 +20,9 @@
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.client.PatchSetInfo;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
 public class PatchSetPublishDetail {
@@ -28,6 +31,8 @@
   protected Change change;
   protected List<PatchLineComment> drafts;
   protected List<PermissionRange> labels;
+  protected List<ApprovalDetail> approvals;
+  protected List<SubmitRecord> submitRecords;
   protected List<PatchSetApproval> given;
   protected boolean canSubmit;
 
@@ -39,6 +44,23 @@
     this.labels = labels;
   }
 
+  public List<ApprovalDetail> getApprovals() {
+    return approvals;
+  }
+
+  public void setApprovals(Collection<ApprovalDetail> list) {
+    approvals = new ArrayList<ApprovalDetail>(list);
+    Collections.sort(approvals, ApprovalDetail.SORT);
+  }
+
+  public void setSubmitRecords(List<SubmitRecord> all) {
+    submitRecords = all;
+  }
+
+  public List<SubmitRecord> getSubmitRecords() {
+    return submitRecords;
+  }
+
   public List<PatchSetApproval> getGiven() {
     return given;
   }
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 20261de..fd40888 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
@@ -15,11 +15,13 @@
 package com.google.gerrit.common.data;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 
 /** A single permission within an {@link AccessSection} of a project. */
 public class Permission implements Comparable<Permission> {
+  public static final String ABANDON = "abandon";
   public static final String CREATE = "create";
   public static final String FORGE_AUTHOR = "forgeAuthor";
   public static final String FORGE_COMMITTER = "forgeCommitter";
@@ -40,6 +42,7 @@
     NAMES_LC = new ArrayList<String>();
     NAMES_LC.add(OWNER.toLowerCase());
     NAMES_LC.add(READ.toLowerCase());
+    NAMES_LC.add(ABANDON.toLowerCase());
     NAMES_LC.add(CREATE.toLowerCase());
     NAMES_LC.add(FORGE_AUTHOR.toLowerCase());
     NAMES_LC.add(FORGE_COMMITTER.toLowerCase());
@@ -73,6 +76,13 @@
     return LABEL + labelName;
   }
 
+  public static boolean canBeOnAllProjects(String ref, String permissionName) {
+    if (AccessSection.ALL.equals(ref)) {
+      return !OWNER.equals(permissionName);
+    }
+    return true;
+  }
+
   protected String name;
   protected boolean exclusiveGroup;
   protected List<PermissionRule> rules;
@@ -208,4 +218,23 @@
     int index = NAMES_LC.indexOf(a.getName().toLowerCase());
     return 0 <= index ? index : NAMES_LC.size();
   }
+
+  @Override
+  public boolean equals(final Object obj) {
+    if (!(obj instanceof Permission)) {
+      return false;
+    }
+
+    final Permission other = (Permission) obj;
+    if (!name.equals(other.name) || exclusiveGroup != other.exclusiveGroup) {
+      return false;
+    }
+    return new HashSet<PermissionRule>(rules)
+        .equals(new HashSet<PermissionRule>(other.rules));
+  }
+
+  @Override
+  public int hashCode() {
+    return name.hashCode();
+  }
 }
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 9b6695e..5960165 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
@@ -257,4 +257,19 @@
     }
     return Integer.parseInt(value);
   }
+
+  @Override
+  public boolean equals(final Object obj) {
+    if (!(obj instanceof PermissionRule)) {
+      return false;
+    }
+    final PermissionRule other = (PermissionRule)obj;
+    return action.equals(other.action) && force == other.force
+        && min == other.min && max == other.max && group.equals(other.group);
+  }
+
+  @Override
+  public int hashCode() {
+    return group.hashCode();
+  }
 }
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 f935c03..1893843 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
@@ -26,6 +26,7 @@
   protected List<AccessSection> local;
   protected Set<String> ownerOf;
   protected boolean isConfigVisible;
+  protected boolean canUpload;
 
   public ProjectAccess() {
   }
@@ -94,4 +95,12 @@
   public void setConfigVisible(boolean isConfigVisible) {
     this.isConfigVisible = isConfigVisible;
   }
+
+  public boolean canUpload() {
+    return canUpload;
+  }
+
+  public void setCanUpload(boolean canUpload) {
+    this.canUpload = canUpload;
+  }
 }
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 1b504b0..13c0a48 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
@@ -14,8 +14,10 @@
 
 package com.google.gerrit.common.data;
 
+import com.google.gerrit.common.audit.Audit;
 import com.google.gerrit.common.auth.SignInRequired;
 import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gwtjsonrpc.common.AsyncCallback;
 import com.google.gwtjsonrpc.common.RemoteJsonService;
@@ -28,14 +30,12 @@
 
 @RpcImpl(version = Version.V2_0)
 public interface ProjectAdminService extends RemoteJsonService {
-  void visibleProjects(AsyncCallback<ProjectList> callback);
-
   void visibleProjectDetails(AsyncCallback<List<ProjectDetail>> callback);
-  void suggestParentCandidates(AsyncCallback<List<Project>> callback);
 
   void projectDetail(Project.NameKey projectName,
       AsyncCallback<ProjectDetail> callback);
 
+  @Audit
   @SignInRequired
   void createNewProject(String projectName, String parentName,
       boolean emptyCommit, boolean permissionsOnly,
@@ -44,22 +44,31 @@
   void projectAccess(Project.NameKey projectName,
       AsyncCallback<ProjectAccess> callback);
 
+  @Audit
   @SignInRequired
   void changeProjectSettings(Project update,
       AsyncCallback<ProjectDetail> callback);
 
+  @Audit
   @SignInRequired
   void changeProjectAccess(Project.NameKey projectName, String baseRevision,
       String message, List<AccessSection> sections,
       AsyncCallback<ProjectAccess> callback);
 
+  @SignInRequired
+  void reviewProjectAccess(Project.NameKey projectName, String baseRevision,
+      String message, List<AccessSection> sections,
+      AsyncCallback<Change.Id> callback);
+
   void listBranches(Project.NameKey projectName,
       AsyncCallback<ListBranchesResult> callback);
 
+  @Audit
   @SignInRequired
   void addBranch(Project.NameKey projectName, String branchName,
       String startingRevision, AsyncCallback<ListBranchesResult> callback);
 
+  @Audit
   @SignInRequired
   void deleteBranch(Project.NameKey projectName, Set<Branch.NameKey> ids,
       AsyncCallback<Set<Branch.NameKey>> callback);
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectList.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectList.java
deleted file mode 100644
index 8511460..0000000
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectList.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2011 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.data;
-
-import com.google.gerrit.reviewdb.client.Project;
-
-import java.util.List;
-
-public class ProjectList {
-  protected List<Project> projects;
-  protected boolean canCreateProject;
-
-  public ProjectList() {
-  }
-
-  public List<Project> getProjects() {
-    return projects;
-  }
-
-  public void setProjects(List<Project> projects) {
-    this.projects = projects;
-  }
-
-  public boolean canCreateProject() {
-    return canCreateProject;
-  }
-
-  public void setCanCreateProject(boolean canCreateProject) {
-    this.canCreateProject = canCreateProject;
-  }
-}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/RefConfigSection.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/RefConfigSection.java
index 490378e..810e906 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/RefConfigSection.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/RefConfigSection.java
@@ -21,6 +21,9 @@
   /** Pattern that matches all branches in a project. */
   public static final String HEADS = "refs/heads/*";
 
+  /** Configuration settings for a project {@code refs/meta/config} */
+  public static final String REF_CONFIG = "refs/meta/config";
+
   /** Prefix that triggers a regular expression pattern. */
   public static final String REGEX_PREFIX = "^";
 
@@ -45,4 +48,17 @@
   public void setName(String name) {
     this.name = name;
   }
+
+  @Override
+  public boolean equals(final Object obj) {
+    if (!(obj instanceof RefConfigSection)) {
+      return false;
+    }
+    return name.equals(((RefConfigSection) obj).name);
+  }
+
+  @Override
+  public int hashCode() {
+    return name.hashCode();
+  }
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ReviewResult.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ReviewResult.java
index 001f9b4..c0bf818 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ReviewResult.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ReviewResult.java
@@ -63,6 +63,9 @@
       /** Review operation invalid because change is closed. */
       CHANGE_IS_CLOSED,
 
+      /** Review operation invalid because change is not abandoned. */
+      CHANGE_NOT_ABANDONED,
+
       /** Not permitted to publish this draft patch set */
       PUBLISH_NOT_PERMITTED,
 
@@ -76,7 +79,10 @@
       NOT_A_DRAFT,
 
       /** Error writing change to git repository */
-      GIT_ERROR
+      GIT_ERROR,
+
+      /** The destination branch does not exist */
+      DEST_BRANCH_NOT_FOUND
     }
 
     protected Type type;
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 5049ba4..365f6a9 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
@@ -68,6 +68,12 @@
       NEED,
 
       /**
+       * The label may be set, but it's neither necessary for submission
+       * nor does it block submission if set.
+       */
+      MAY,
+
+      /**
        * The label is required for submission, but is impossible to complete.
        * The likely cause is access has not been granted correctly by the
        * project owner or site administrator.
@@ -78,5 +84,34 @@
     public String label;
     public Status status;
     public Account.Id appliedBy;
+
+    @Override
+    public String toString() {
+      StringBuilder sb = new StringBuilder();
+      sb.append(label).append(": ").append(status);
+      if (appliedBy != null) {
+        sb.append(" by ").append(appliedBy);
+      }
+      return sb.toString();
+    }
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append(status);
+    if (status == Status.RULE_ERROR && errorMessage != null) {
+      sb.append('(').append(errorMessage).append(')');
+    }
+    sb.append('[');
+    if (labels != null) {
+      String delimiter = "";
+      for (Label label : labels) {
+        sb.append(delimiter).append(label);
+        delimiter = ", ";
+      }
+    }
+    sb.append(']');
+    return sb.toString();
   }
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java
index 85518b2..7205b74 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java
@@ -26,15 +26,19 @@
 
 @RpcImpl(version = Version.V2_0)
 public interface SuggestService extends RemoteJsonService {
-  void suggestProjectNameKey(String query, int limit,
-      AsyncCallback<List<Project.NameKey>> callback);
-
   void suggestAccount(String query, Boolean enabled, int limit,
       AsyncCallback<List<AccountInfo>> callback);
 
+  /**
+   * @see #suggestAccountGroup(com.google.gerrit.reviewdb.client.Project.NameKey, String, int, AsyncCallback)
+   */
+  @Deprecated
   void suggestAccountGroup(String query, int limit,
       AsyncCallback<List<GroupReference>> callback);
 
+  void suggestAccountGroupForProject(Project.NameKey project, String query,
+      int limit, AsyncCallback<List<GroupReference>> callback);
+
   /**
    * @see #suggestChangeReviewer(com.google.gerrit.reviewdb.client.Change.Id, String, int, AsyncCallback)
    */
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/SystemInfoService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/SystemInfoService.java
index 78ccca1..4a45350 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/SystemInfoService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/SystemInfoService.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.common.data;
 
 import com.google.gerrit.common.auth.SignInRequired;
-import com.google.gerrit.reviewdb.client.ContributorAgreement;
 import com.google.gwtjsonrpc.common.AsyncCallback;
 import com.google.gwtjsonrpc.common.AllowCrossSiteRequest;
 import com.google.gwtjsonrpc.common.RemoteJsonService;
diff --git a/gerrit-ehcache/.settings/org.eclipse.core.resources.prefs b/gerrit-ehcache/.settings/org.eclipse.core.resources.prefs
deleted file mode 100644
index 82eb859..0000000
--- a/gerrit-ehcache/.settings/org.eclipse.core.resources.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Tue Sep 02 16:59:24 PDT 2008
-eclipse.preferences.version=1
-encoding/<project>=UTF-8
diff --git a/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/EhcachePoolImpl.java b/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/EhcachePoolImpl.java
deleted file mode 100644
index c25c381..0000000
--- a/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/EhcachePoolImpl.java
+++ /dev/null
@@ -1,271 +0,0 @@
-// Copyright (C) 2009 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.ehcache;
-
-import static java.util.concurrent.TimeUnit.MINUTES;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import com.google.gerrit.lifecycle.LifecycleListener;
-import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.CachePool;
-import com.google.gerrit.server.cache.CacheProvider;
-import com.google.gerrit.server.cache.EntryCreator;
-import com.google.gerrit.server.cache.EvictionPolicy;
-import com.google.gerrit.server.cache.ProxyCache;
-import com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import net.sf.ehcache.CacheManager;
-import net.sf.ehcache.Ehcache;
-import net.sf.ehcache.config.CacheConfiguration;
-import net.sf.ehcache.config.Configuration;
-import net.sf.ehcache.config.DiskStoreConfiguration;
-import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
-
-import org.eclipse.jgit.lib.Config;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
-
-/** Pool of all declared caches created by {@link CacheModule}s. */
-@Singleton
-public class EhcachePoolImpl implements CachePool {
-  private static final Logger log =
-      LoggerFactory.getLogger(EhcachePoolImpl.class);
-
-  public static class Module extends LifecycleModule {
-    @Override
-    protected void configure() {
-      bind(CachePool.class).to(EhcachePoolImpl.class);
-      bind(EhcachePoolImpl.class);
-      listener().to(EhcachePoolImpl.Lifecycle.class);
-    }
-  }
-
-  public static class Lifecycle implements LifecycleListener {
-    private final EhcachePoolImpl cachePool;
-
-    @Inject
-    Lifecycle(final EhcachePoolImpl cachePool) {
-      this.cachePool = cachePool;
-    }
-
-    @Override
-    public void start() {
-      cachePool.start();
-    }
-
-    @Override
-    public void stop() {
-      cachePool.stop();
-    }
-  }
-
-  private final Config config;
-  private final SitePaths site;
-
-  private final Object lock = new Object();
-  private final Map<String, CacheProvider<?, ?>> caches;
-  private CacheManager manager;
-
-  @Inject
-  EhcachePoolImpl(@GerritServerConfig final Config cfg, final SitePaths site) {
-    this.config = cfg;
-    this.site = site;
-    this.caches = new HashMap<String, CacheProvider<?, ?>>();
-  }
-
-  private void start() {
-    synchronized (lock) {
-      if (manager != null) {
-        throw new IllegalStateException("Cache pool has already been started");
-      }
-
-      try {
-        System.setProperty("net.sf.ehcache.skipUpdateCheck", "" + true);
-      } catch (SecurityException e) {
-        // Ignore it, the system is just going to ping some external page
-        // using a background thread and there's not much we can do about
-        // it now.
-      }
-
-      manager = new CacheManager(new Factory().toConfiguration());
-      for (CacheProvider<?, ?> p : caches.values()) {
-        Ehcache eh = manager.getEhcache(p.getName());
-        EntryCreator<?, ?> c = p.getEntryCreator();
-        if (c != null) {
-          p.bind(new PopulatingCache(eh, c));
-        } else {
-          p.bind(new SimpleCache(eh));
-        }
-      }
-    }
-  }
-
-  private void stop() {
-    synchronized (lock) {
-      if (manager != null) {
-        manager.shutdown();
-      }
-    }
-  }
-
-  /** <i>Discouraged</i> Get the underlying cache descriptions, for statistics. */
-  public CacheManager getCacheManager() {
-    synchronized (lock) {
-      return manager;
-    }
-  }
-
-  public <K, V> ProxyCache<K, V> register(final CacheProvider<K, V> provider) {
-    synchronized (lock) {
-      if (manager != null) {
-        throw new IllegalStateException("Cache pool has already been started");
-      }
-
-      final String n = provider.getName();
-      if (caches.containsKey(n) && caches.get(n) != provider) {
-        throw new IllegalStateException("Cache \"" + n + "\" already defined");
-      }
-      caches.put(n, provider);
-      return new ProxyCache<K, V>();
-    }
-  }
-
-  private class Factory {
-    private static final int MB = 1024 * 1024;
-    private final Configuration mgr = new Configuration();
-
-    Configuration toConfiguration() {
-      configureDiskStore();
-      configureDefaultCache();
-
-      for (CacheProvider<?, ?> p : caches.values()) {
-        final String name = p.getName();
-        final CacheConfiguration c = newCache(name);
-        c.setMemoryStoreEvictionPolicyFromObject(toPolicy(p.evictionPolicy()));
-
-        c.setMaxElementsInMemory(getInt(name, "memorylimit", p.memoryLimit()));
-
-        c.setTimeToIdleSeconds(0);
-        c.setTimeToLiveSeconds(getSeconds(name, "maxage", p.maxAge()));
-        c.setEternal(c.getTimeToLiveSeconds() == 0);
-
-        if (p.disk() && mgr.getDiskStoreConfiguration() != null) {
-          c.setMaxElementsOnDisk(getInt(name, "disklimit", p.diskLimit()));
-
-          int v = c.getDiskSpoolBufferSizeMB() * MB;
-          v = getInt(name, "diskbuffer", v) / MB;
-          c.setDiskSpoolBufferSizeMB(Math.max(1, v));
-          c.setOverflowToDisk(c.getMaxElementsOnDisk() > 0);
-          c.setDiskPersistent(c.getMaxElementsOnDisk() > 0);
-        }
-
-        mgr.addCache(c);
-      }
-
-      return mgr;
-    }
-
-    private MemoryStoreEvictionPolicy toPolicy(final EvictionPolicy policy) {
-      switch (policy) {
-        case LFU:
-          return MemoryStoreEvictionPolicy.LFU;
-
-        case LRU:
-          return MemoryStoreEvictionPolicy.LRU;
-
-        default:
-          throw new IllegalArgumentException("Unsupported " + policy);
-      }
-    }
-
-    private int getInt(String n, String s, int d) {
-      return config.getInt("cache", n, s, d);
-    }
-
-    private long getSeconds(String n, String s, long d) {
-      d = MINUTES.convert(d, SECONDS);
-      long m = ConfigUtil.getTimeUnit(config, "cache", n, s, d, MINUTES);
-      return SECONDS.convert(m, MINUTES);
-    }
-
-    private void configureDiskStore() {
-      boolean needDisk = false;
-      for (CacheProvider<?, ?> p : caches.values()) {
-        if (p.disk()) {
-          needDisk = true;
-          break;
-        }
-      }
-      if (!needDisk) {
-        return;
-      }
-
-      File loc = site.resolve(config.getString("cache", null, "directory"));
-      if (loc == null) {
-      } else if (loc.exists() || loc.mkdirs()) {
-        if (loc.canWrite()) {
-          final DiskStoreConfiguration c = new DiskStoreConfiguration();
-          c.setPath(loc.getAbsolutePath());
-          mgr.addDiskStore(c);
-          log.info("Enabling disk cache " + loc.getAbsolutePath());
-        } else {
-          log.warn("Can't write to disk cache: " + loc.getAbsolutePath());
-        }
-      } else {
-        log.warn("Can't create disk cache: " + loc.getAbsolutePath());
-      }
-    }
-
-    private CacheConfiguration newConfiguration() {
-      CacheConfiguration c = new CacheConfiguration();
-
-      c.setMaxElementsInMemory(1024);
-      c.setMemoryStoreEvictionPolicyFromObject(MemoryStoreEvictionPolicy.LFU);
-
-      c.setTimeToIdleSeconds(0);
-      c.setTimeToLiveSeconds(0 /* infinite */);
-      c.setEternal(true);
-
-      if (mgr.getDiskStoreConfiguration() != null) {
-        c.setMaxElementsOnDisk(16384);
-        c.setOverflowToDisk(false);
-        c.setDiskPersistent(false);
-
-        c.setDiskSpoolBufferSizeMB(5);
-        c.setDiskExpiryThreadIntervalSeconds(60 * 60);
-      }
-      return c;
-    }
-
-    private void configureDefaultCache() {
-      mgr.setDefaultCacheConfiguration(newConfiguration());
-    }
-
-    private CacheConfiguration newCache(final String name) {
-      CacheConfiguration c = newConfiguration();
-      c.setName(name);
-      return c;
-    }
-  }
-}
diff --git a/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/PopulatingCache.java b/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/PopulatingCache.java
deleted file mode 100644
index f5c6c45..0000000
--- a/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/PopulatingCache.java
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.ehcache;
-
-import com.google.gerrit.server.cache.Cache;
-import com.google.gerrit.server.cache.EntryCreator;
-
-import net.sf.ehcache.CacheException;
-import net.sf.ehcache.Ehcache;
-import net.sf.ehcache.Element;
-import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A decorator for {@link Cache} which automatically constructs missing entries.
- * <p>
- * On a cache miss {@link EntryCreator#createEntry(Object)} is invoked, allowing
- * the application specific subclass to compute the entry and return it for
- * caching. During a miss the cache takes a lock related to the missing key,
- * ensuring that at most one thread performs the creation work, and other
- * threads wait for the result. Concurrent creations are possible if two
- * different keys miss and hash to different locks in the internal lock table.
- *
- * @param <K> type of key used to name cache entries.
- * @param <V> type of value stored within a cache entry.
- */
-class PopulatingCache<K, V> implements Cache<K, V> {
-  private static final Logger log =
-      LoggerFactory.getLogger(PopulatingCache.class);
-
-  private final net.sf.ehcache.constructs.blocking.SelfPopulatingCache self;
-  private final EntryCreator<K, V> creator;
-
-  PopulatingCache(Ehcache s, EntryCreator<K, V> entryCreator) {
-    creator = entryCreator;
-    final CacheEntryFactory f = new CacheEntryFactory() {
-      @SuppressWarnings("unchecked")
-      @Override
-      public Object createEntry(Object key) throws Exception {
-        return creator.createEntry((K) key);
-      }
-    };
-    self = new net.sf.ehcache.constructs.blocking.SelfPopulatingCache(s, f);
-  }
-
-  /**
-   * Get the element from the cache, or {@link EntryCreator#missing(Object)} if not found.
-   * <p>
-   * The {@link EntryCreator#missing(Object)} method is only invoked if:
-   * <ul>
-   * <li>{@code key == null}, in which case the application should return a
-   * suitable return value that callers can accept, or throw a RuntimeException.
-   * <li>{@code createEntry(key)} threw an exception, in which case the entry
-   * was not stored in the cache. An entry was recorded in the application log,
-   * but a return value is still required.
-   * <li>The cache has been shutdown, and access is forbidden.
-   * </ul>
-   *
-   * @param key key to locate.
-   * @return either the cached entry, or {@code missing(key)} if not found.
-   */
-  @SuppressWarnings("unchecked")
-  public V get(final K key) {
-    if (key == null) {
-      return creator.missing(key);
-    }
-
-    final Element m;
-    try {
-      m = self.get(key);
-    } catch (IllegalStateException err) {
-      log.error("Cannot lookup " + key + " in \"" + self.getName() + "\"", err);
-      return creator.missing(key);
-    } catch (CacheException err) {
-      log.error("Cannot lookup " + key + " in \"" + self.getName() + "\"", err);
-      return creator.missing(key);
-    }
-    return m != null ? (V) m.getObjectValue() : creator.missing(key);
-  }
-
-  public void remove(final K key) {
-    if (key != null) {
-      self.remove(key);
-    }
-  }
-
-  /** Remove all cached items, forcing them to be created again on demand. */
-  public void removeAll() {
-    self.removeAll();
-  }
-
-  public void put(K key, V value) {
-    self.put(new Element(key, value));
-  }
-
-  @Override
-  public String toString() {
-    return "Cache[" + self.getName() + "]";
-  }
-}
diff --git a/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/SimpleCache.java b/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/SimpleCache.java
deleted file mode 100644
index e4428e3..0000000
--- a/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/SimpleCache.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (C) 2009 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.ehcache;
-
-import com.google.gerrit.server.cache.Cache;
-
-import net.sf.ehcache.CacheException;
-import net.sf.ehcache.Ehcache;
-import net.sf.ehcache.Element;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A fast in-memory and/or on-disk based cache.
- *
- * @type <K> type of key used to lookup entries in the cache.
- * @type <V> type of value stored within each cache entry.
- */
-final class SimpleCache<K, V> implements Cache<K, V> {
-  private static final Logger log = LoggerFactory.getLogger(SimpleCache.class);
-
-  private final Ehcache self;
-
-  SimpleCache(final Ehcache self) {
-    this.self = self;
-  }
-
-  Ehcache getEhcache() {
-    return self;
-  }
-
-  @SuppressWarnings("unchecked")
-  public V get(final K key) {
-    if (key == null) {
-      return null;
-    }
-    final Element m;
-    try {
-      m = self.get(key);
-    } catch (IllegalStateException err) {
-      log.error("Cannot lookup " + key + " in \"" + self.getName() + "\"", err);
-      return null;
-    } catch (CacheException err) {
-      log.error("Cannot lookup " + key + " in \"" + self.getName() + "\"", err);
-      return null;
-    }
-    return m != null ? (V) m.getObjectValue() : null;
-  }
-
-  public void put(final K key, final V value) {
-    self.put(new Element(key, value));
-  }
-
-  public void remove(final K key) {
-    if (key != null) {
-      self.remove(key);
-    }
-  }
-
-  public void removeAll() {
-    self.removeAll();
-  }
-
-  @Override
-  public String toString() {
-    return "Cache[" + self.getName() + "]";
-  }
-}
diff --git a/gerrit-ehcache/.gitignore b/gerrit-extension-api/.gitignore
similarity index 80%
copy from gerrit-ehcache/.gitignore
copy to gerrit-extension-api/.gitignore
index 20251d4..4e1ec9c 100644
--- a/gerrit-ehcache/.gitignore
+++ b/gerrit-extension-api/.gitignore
@@ -1,5 +1,6 @@
 /target
 /.classpath
 /.project
-/.settings/org.eclipse.m2e.core.prefs
 /.settings/org.maven.ide.eclipse.prefs
+/.settings/org.eclipse.m2e.core.prefs
+/gerrit-extension-api.iml
diff --git a/gerrit-extension-api/.settings/org.eclipse.core.resources.prefs b/gerrit-extension-api/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..f9fe345
--- /dev/null
+++ b/gerrit-extension-api/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/test/java=UTF-8
+encoding/<project>=UTF-8
diff --git a/gerrit-ehcache/.settings/org.eclipse.core.runtime.prefs b/gerrit-extension-api/.settings/org.eclipse.core.runtime.prefs
similarity index 100%
copy from gerrit-ehcache/.settings/org.eclipse.core.runtime.prefs
copy to gerrit-extension-api/.settings/org.eclipse.core.runtime.prefs
diff --git a/gerrit-ehcache/.settings/org.eclipse.jdt.core.prefs b/gerrit-extension-api/.settings/org.eclipse.jdt.core.prefs
similarity index 99%
copy from gerrit-ehcache/.settings/org.eclipse.jdt.core.prefs
copy to gerrit-extension-api/.settings/org.eclipse.jdt.core.prefs
index e89c048..470942d 100644
--- a/gerrit-ehcache/.settings/org.eclipse.jdt.core.prefs
+++ b/gerrit-extension-api/.settings/org.eclipse.jdt.core.prefs
@@ -1,4 +1,4 @@
-#Thu Jan 19 12:55:44 PST 2012
+#Thu Jul 28 11:02:36 PDT 2011
 eclipse.preferences.version=1
 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
diff --git a/gerrit-ehcache/.settings/org.eclipse.jdt.ui.prefs b/gerrit-extension-api/.settings/org.eclipse.jdt.ui.prefs
similarity index 100%
copy from gerrit-ehcache/.settings/org.eclipse.jdt.ui.prefs
copy to gerrit-extension-api/.settings/org.eclipse.jdt.ui.prefs
diff --git a/gerrit-extension-api/pom.xml b/gerrit-extension-api/pom.xml
new file mode 100644
index 0000000..ff672d5
--- /dev/null
+++ b/gerrit-extension-api/pom.xml
@@ -0,0 +1,73 @@
+<?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>
+
+  <parent>
+    <groupId>com.google.gerrit</groupId>
+    <artifactId>gerrit-parent</artifactId>
+    <version>2.5-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>gerrit-extension-api</artifactId>
+  <name>Gerrit Code Review - Extension API</name>
+
+  <description>
+    Interfaces describing the extension API
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.inject</groupId>
+      <artifactId>guice</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.google.inject.extensions</groupId>
+      <artifactId>guice-servlet</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.tomcat</groupId>
+      <artifactId>servlet-api</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <configuration>
+          <createSourcesJar>true</createSourcesJar>
+          <shadedArtifactAttached>true</shadedArtifactAttached>
+          <shadedClassifierName>all</shadedClassifierName>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/Export.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/Export.java
new file mode 100644
index 0000000..4811e407
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/Export.java
@@ -0,0 +1,52 @@
+// 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.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 auto-registered, exported types.
+ * <p>
+ * Plugins or extensions using auto-registration should apply this annotation to
+ * any non-abstract class they want exported for access.
+ * <p>
+ * For SSH commands the @Export annotation names the subcommand:
+ *
+ * <pre>
+ *   @Export("print")
+ *   class MyCommand extends SshCommand {
+ * </pre>
+ *
+ * For HTTP servlets, the @Export annotation names the URL the servlet is bound
+ * to, relative to the plugin or extension's namespace within the Gerrit
+ * container.
+ *
+ * <pre>
+ *  @Export("/index.html")
+ *  class ShowIndexHtml extends HttpServlet {
+ * </pre>
+ */
+@Target({ElementType.TYPE})
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface Export {
+  String value();
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/ExportImpl.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/ExportImpl.java
new file mode 100644
index 0000000..a3e72bc
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/ExportImpl.java
@@ -0,0 +1,52 @@
+// 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.extensions.annotations;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+
+final class ExportImpl implements Export, Serializable {
+  private static final long serialVersionUID = 0;
+  private final String value;
+
+  ExportImpl(String value) {
+    this.value = value;
+  }
+
+  @Override
+  public Class<? extends Annotation> annotationType() {
+    return Export.class;
+  }
+
+  @Override
+  public String value() {
+    return value;
+  }
+
+  @Override
+  public int hashCode() {
+    return (127 * "value".hashCode()) ^ value.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    return o instanceof Export && value.equals(((Export) o).value());
+  }
+
+  @Override
+  public String toString() {
+    return "@" + Export.class.getName() + "(value=" + value + ")";
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeRestoreEvent.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/Exports.java
similarity index 60%
copy from gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeRestoreEvent.java
copy to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/Exports.java
index 1a2922b..c48bcfb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeRestoreEvent.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/Exports.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 The Android Open Source Project
+// 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.
@@ -12,12 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.events;
+package com.google.gerrit.extensions.annotations;
 
-public class ChangeRestoreEvent extends ChangeEvent {
-    public final String type = "change-restored";
-    public ChangeAttribute change;
-    public PatchSetAttribute patchSet;
-    public AccountAttribute restorer;
-    public String reason;
+/** Static constructors for {@link Export} annotations. */
+public final class Exports {
+  /** Create an annotation to export under a specific name. */
+  public static Export named(String name) {
+    return new ExportImpl(name);
+  }
+
+  private Exports() {
+  }
 }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/ExtensionPoint.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/ExtensionPoint.java
new file mode 100644
index 0000000..4799f5e
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/ExtensionPoint.java
@@ -0,0 +1,41 @@
+// 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.extensions.annotations;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.inject.BindingAnnotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for interfaces that accept auto-registered implementations.
+ * <p>
+ * Interfaces that accept automatically registered implementations into their
+ * {@link DynamicSet} must be tagged with this annotation.
+ * <p>
+ * Plugins or extensions that implement an {@code @ExtensionPoint} interface
+ * should use the {@link Listen} annotation to automatically register.
+ *
+ * @see Listen
+ */
+@Target({ElementType.TYPE})
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface ExtensionPoint {
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/Listen.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/Listen.java
new file mode 100644
index 0000000..e4ba931
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/Listen.java
@@ -0,0 +1,39 @@
+// 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.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 for auto-registered extension point implementations.
+ * <p>
+ * Plugins or extensions using auto-registration should apply this annotation to
+ * any non-abstract class that implements an unnamed extension point, such as a
+ * notification listener. Gerrit will automatically determine which extension
+ * points to apply based on the interfaces the type implements.
+ *
+ * @see Export
+ */
+@Target({ElementType.TYPE})
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface Listen {
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginData.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginData.java
new file mode 100644
index 0000000..bf2b09a
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginData.java
@@ -0,0 +1,42 @@
+// 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.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;
+
+/**
+ * Local path where a plugin can store its own private data.
+ * <p>
+ * A plugin or extension may receive this string by Guice injection to discover
+ * a directory where it can store configuration or other data that is private:
+ *
+ * <pre>
+ * @Inject
+ * MyType(@PluginData java.io.File myDir) {
+ *   new FileInputStream(new File(myDir, &quot;my.config&quot;));
+ * }
+ * </pre>
+ */
+@Target({ElementType.PARAMETER, ElementType.FIELD})
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface PluginData {
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginName.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginName.java
new file mode 100644
index 0000000..672bab2
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginName.java
@@ -0,0 +1,42 @@
+// 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.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 or extension name.
+ * <p>
+ * A plugin or extension may receive this string by Guice injection to discover
+ * the name that an administrator has installed the plugin or extension under:
+ *
+ * <pre>
+ *  @Inject
+ *  MyType(@PluginName String myName) {
+ *  ...
+ *  }
+ * </pre>
+ */
+@Target({ElementType.PARAMETER, ElementType.FIELD})
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface PluginName {
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AdminCommand.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/RequiresCapability.java
similarity index 65%
rename from gerrit-sshd/src/main/java/com/google/gerrit/sshd/AdminCommand.java
rename to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/RequiresCapability.java
index adaf646..382f4ea 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AdminCommand.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/RequiresCapability.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 The Android Open Source Project
+// 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.
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.sshd;
+package com.google.gerrit.extensions.annotations;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -21,12 +21,11 @@
 import java.lang.annotation.Target;
 
 /**
- * Annotation tagged on a concrete Command that requires administrator access.
- * <p>
- * Currently this annotation is only enforced by DispatchCommand after it has
- * created the command object, but before it populates it or starts execution.
+ * Annotation on {@link SshCommand} or {@link RestApiServlet} declaring a
+ * capability must be granted.
  */
-@Target( {ElementType.TYPE})
+@Target({ElementType.TYPE})
 @Retention(RUNTIME)
-public @interface AdminCommand {
+public @interface RequiresCapability {
+  String value();
 }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GitReferenceUpdatedListener.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GitReferenceUpdatedListener.java
new file mode 100644
index 0000000..438500d
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GitReferenceUpdatedListener.java
@@ -0,0 +1,34 @@
+// 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.extensions.events;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+import java.util.List;
+
+/** Notified when one or more references are modified. */
+@ExtensionPoint
+public interface GitReferenceUpdatedListener {
+  public interface Update {
+    String getRefName();
+  }
+
+  public interface Event {
+    String getProjectName();
+    List<Update> getUpdates();
+  }
+
+  void onGitReferenceUpdated(Event event);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/lifecycle/LifecycleListener.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/LifecycleListener.java
similarity index 87%
rename from gerrit-server/src/main/java/com/google/gerrit/lifecycle/LifecycleListener.java
rename to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/LifecycleListener.java
index e6b06ef..93da347 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/lifecycle/LifecycleListener.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/LifecycleListener.java
@@ -12,11 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.lifecycle;
+package com.google.gerrit.extensions.events;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
 
 import java.util.EventListener;
 
 /** Listener interested in server startup and shutdown events. */
+@ExtensionPoint
 public interface LifecycleListener extends EventListener {
   /** Invoke when the server is starting. */
   public void start();
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/NewProjectCreatedListener.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/NewProjectCreatedListener.java
new file mode 100644
index 0000000..7eed7d4
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/NewProjectCreatedListener.java
@@ -0,0 +1,29 @@
+// 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.extensions.events;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+
+/** Notified whenever a project is created on the master. */
+@ExtensionPoint
+public interface NewProjectCreatedListener {
+  public interface Event {
+    String getProjectName();
+    String getHeadName();
+  }
+
+  void onNewProjectCreated(Event event);
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicMap.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicMap.java
new file mode 100644
index 0000000..40bbb80
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicMap.java
@@ -0,0 +1,163 @@
+// 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.extensions.registration;
+
+import com.google.inject.Binder;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import com.google.inject.Scopes;
+import com.google.inject.TypeLiteral;
+import com.google.inject.util.Types;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * A map of members that can be modified as plugins reload.
+ * <p>
+ * Maps index their members by plugin name and export name.
+ * <p>
+ * DynamicMaps are always mapped as singletons in Guice. Maps store Providers
+ * internally, and resolve the provider to an instance on demand. This enables
+ * registrations to decide between singleton and non-singleton members.
+ */
+public abstract class DynamicMap<T> {
+  /**
+   * Declare a singleton {@code DynamicMap<T>} with a binder.
+   * <p>
+   * Maps must be defined in a Guice module before they can be bound:
+   *
+   * <pre>
+   * DynamicMap.mapOf(binder(), Interface.class);
+   * bind(Interface.class)
+   *   .annotatedWith(Exports.named(&quot;foo&quot;))
+   *   .to(Impl.class);
+   * </pre>
+   *
+   * @param binder a new binder created in the module.
+   * @param member type of value in the map.
+   */
+  public static <T> void mapOf(Binder binder, Class<T> member) {
+    mapOf(binder, TypeLiteral.get(member));
+  }
+
+  /**
+   * Declare a singleton {@code DynamicMap<T>} with a binder.
+   * <p>
+   * Maps must be defined in a Guice module before they can be bound:
+   *
+   * <pre>
+   * DynamicMap.mapOf(binder(), new TypeLiteral<Thing<Bar>>(){});
+   * bind(new TypeLiteral<Thing<Bar>>() {})
+   *   .annotatedWith(Exports.named(&quot;foo&quot;))
+   *   .to(Impl.class);
+   * </pre>
+   *
+   * @param binder a new binder created in the module.
+   * @param member type of value in the map.
+   */
+  public static <T> void mapOf(Binder binder, TypeLiteral<T> member) {
+    @SuppressWarnings("unchecked")
+    Key<DynamicMap<T>> key = (Key<DynamicMap<T>>) Key.get(
+        Types.newParameterizedType(DynamicMap.class, member.getType()));
+    binder.bind(key)
+        .toProvider(new DynamicMapProvider<T>(member))
+        .in(Scopes.SINGLETON);
+  }
+
+  final ConcurrentMap<NamePair, Provider<T>> items;
+
+  DynamicMap() {
+    items = new ConcurrentHashMap<NamePair, Provider<T>>(
+        16 /* initial size */,
+        0.75f /* load factor */,
+        1 /* concurrency level of 1, load/unload is single threaded */);
+  }
+
+  /**
+   * Lookup an implementation by name.
+   *
+   * @param pluginName local name of the plugin providing the item.
+   * @param exportName name the plugin exports the item as.
+   * @return the implementation. Null if the plugin is not running, or if the
+   *         plugin does not export this name.
+   * @throws ProvisionException if the registered provider is unable to obtain
+   *         an instance of the requested implementation.
+   */
+  public T get(String pluginName, String exportName) {
+    Provider<T> p = items.get(new NamePair(pluginName, exportName));
+    return p != null ? p.get() : null;
+  }
+
+  /**
+   * Get the names of all running plugins supplying this type.
+   *
+   * @return sorted set of active plugins that supply at least one item.
+   */
+  public SortedSet<String> plugins() {
+    SortedSet<String> r = new TreeSet<String>();
+    for (NamePair p : items.keySet()) {
+      r.add(p.pluginName);
+    }
+    return Collections.unmodifiableSortedSet(r);
+  }
+
+  /**
+   * Get the items exported by a single plugin.
+   *
+   * @param pluginName name of the plugin.
+   * @return items exported by a plugin, keyed by the export name.
+   */
+  public SortedMap<String, Provider<T>> byPlugin(String pluginName) {
+    SortedMap<String, Provider<T>> r = new TreeMap<String, Provider<T>>();
+    for (Map.Entry<NamePair, Provider<T>> e : items.entrySet()) {
+      if (e.getKey().pluginName.equals(pluginName)) {
+        r.put(e.getKey().exportName, e.getValue());
+      }
+    }
+    return Collections.unmodifiableSortedMap(r);
+  }
+
+  static class NamePair {
+    private final String pluginName;
+    private final String exportName;
+
+    NamePair(String pn, String en) {
+      this.pluginName = pn;
+      this.exportName = en;
+    }
+
+    @Override
+    public int hashCode() {
+      return pluginName.hashCode() * 31 + exportName.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other instanceof NamePair) {
+        NamePair np = (NamePair) other;
+        return pluginName.equals(np.pluginName)
+            && exportName.equals(np.exportName);
+      }
+      return false;
+    }
+  }
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicMapProvider.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicMapProvider.java
new file mode 100644
index 0000000..c6e4701
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicMapProvider.java
@@ -0,0 +1,48 @@
+// 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.extensions.registration;
+
+import com.google.inject.Binding;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+
+import java.util.List;
+
+class DynamicMapProvider<T> implements Provider<DynamicMap<T>> {
+  private final TypeLiteral<T> type;
+
+  @Inject
+  private Injector injector;
+
+  DynamicMapProvider(TypeLiteral<T> type) {
+    this.type = type;
+  }
+
+  public DynamicMap<T> get() {
+    PrivateInternals_DynamicMapImpl<T> m =
+        new PrivateInternals_DynamicMapImpl<T>();
+    List<Binding<T>> bindings = injector.findBindingsByType(type);
+    if (bindings != null) {
+      for (Binding<T> b : bindings) {
+        if (b.getKey().getAnnotation() != null) {
+          m.put("gerrit", b.getKey(), b.getProvider());
+        }
+      }
+    }
+    return m;
+  }
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicSet.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicSet.java
new file mode 100644
index 0000000..ec34887
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicSet.java
@@ -0,0 +1,253 @@
+// 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.extensions.registration;
+
+import com.google.inject.Binder;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import com.google.inject.Scopes;
+import com.google.inject.TypeLiteral;
+import com.google.inject.binder.LinkedBindingBuilder;
+import com.google.inject.internal.UniqueAnnotations;
+import com.google.inject.name.Named;
+import com.google.inject.util.Providers;
+import com.google.inject.util.Types;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A set of members that can be modified as plugins reload.
+ * <p>
+ * DynamicSets are always mapped as singletons in Guice. Sets store Providers
+ * internally, and resolve the provider to an instance on demand. This enables
+ * registrations to decide between singleton and non-singleton members.
+ */
+public class DynamicSet<T> implements Iterable<T> {
+  /**
+   * Declare a singleton {@code DynamicSet<T>} with a binder.
+   * <p>
+   * Sets must be defined in a Guice module before they can be bound:
+   * <pre>
+   *   DynamicSet.setOf(binder(), Interface.class);
+   *   DynamicSet.bind(binder(), Interface.class).to(Impl.class);
+   * </pre>
+   *
+   * @param binder a new binder created in the module.
+   * @param member type of entry in the set.
+   */
+  public static <T> void setOf(Binder binder, Class<T> member) {
+    setOf(binder, TypeLiteral.get(member));
+  }
+
+  /**
+   * Declare a singleton {@code DynamicSet<T>} with a binder.
+   * <p>
+   * Sets must be defined in a Guice module before they can be bound:
+   * <pre>
+   *   DynamicSet.setOf(binder(), new TypeLiteral<Thing<Foo>>() {});
+   * </pre>
+   *
+   * @param binder a new binder created in the module.
+   * @param member type of entry in the set.
+   */
+  public static <T> void setOf(Binder binder, TypeLiteral<T> member) {
+    @SuppressWarnings("unchecked")
+    Key<DynamicSet<T>> key = (Key<DynamicSet<T>>) Key.get(
+        Types.newParameterizedType(DynamicSet.class, member.getType()));
+    binder.bind(key)
+      .toProvider(new DynamicSetProvider<T>(member))
+      .in(Scopes.SINGLETON);
+  }
+
+  /**
+   * Bind one implementation into the set using a unique annotation.
+   *
+   * @param binder a new binder created in the module.
+   * @param type type of entries in the set.
+   * @return a binder to continue configuring the new set member.
+   */
+  public static <T> LinkedBindingBuilder<T> bind(Binder binder, Class<T> type) {
+    return bind(binder, TypeLiteral.get(type));
+  }
+
+  /**
+   * Bind one implementation into the set using a unique annotation.
+   *
+   * @param binder a new binder created in the module.
+   * @param type type of entries in the set.
+   * @return a binder to continue configuring the new set member.
+   */
+  public static <T> LinkedBindingBuilder<T> bind(Binder binder, TypeLiteral<T> type) {
+    return binder.bind(type).annotatedWith(UniqueAnnotations.create());
+  }
+
+  /**
+   * Bind a named implementation into the set.
+   *
+   * @param binder a new binder created in the module.
+   * @param type type of entries in the set.
+   * @param name {@code @Named} annotation to apply instead of a unique
+   *        annotation.
+   * @return a binder to continue configuring the new set member.
+   */
+  public static <T> LinkedBindingBuilder<T> bind(Binder binder,
+      Class<T> type,
+      Named name) {
+    return bind(binder, TypeLiteral.get(type));
+  }
+
+  /**
+   * Bind a named implementation into the set.
+   *
+   * @param binder a new binder created in the module.
+   * @param type type of entries in the set.
+   * @param name {@code @Named} annotation to apply instead of a unique
+   *        annotation.
+   * @return a binder to continue configuring the new set member.
+   */
+  public static <T> LinkedBindingBuilder<T> bind(Binder binder,
+      TypeLiteral<T> type,
+      Named name) {
+    return binder.bind(type).annotatedWith(name);
+  }
+
+  private final CopyOnWriteArrayList<AtomicReference<Provider<T>>> items;
+
+  DynamicSet(Collection<AtomicReference<Provider<T>>> base) {
+    items = new CopyOnWriteArrayList<AtomicReference<Provider<T>>>(base);
+  }
+
+  @Override
+  public Iterator<T> iterator() {
+    final Iterator<AtomicReference<Provider<T>>> itr = items.iterator();
+    return new Iterator<T>() {
+      private T next;
+
+      @Override
+      public boolean hasNext() {
+        while (next == null && itr.hasNext()) {
+          Provider<T> p = itr.next().get();
+          if (p != null) {
+            try {
+              next = p.get();
+            } catch (RuntimeException e) {
+              // TODO Log failed member of DynamicSet.
+            }
+          }
+        }
+        return next != null;
+      }
+
+      @Override
+      public T next() {
+        if (hasNext()) {
+          T result = next;
+          next = null;
+          return result;
+        }
+        throw new NoSuchElementException();
+      }
+
+      @Override
+      public void remove() {
+        throw new UnsupportedOperationException();
+      }
+    };
+  }
+
+  /**
+   * Add one new element to the set.
+   *
+   * @param item the item to add to the collection. Must not be null.
+   * @return handle to remove the item at a later point in time.
+   */
+  public RegistrationHandle add(final T item) {
+    return add(Providers.of(item));
+  }
+
+  /**
+   * Add one new element to the set.
+   *
+   * @param item the item to add to the collection. Must not be null.
+   * @return handle to remove the item at a later point in time.
+   */
+  public RegistrationHandle add(final Provider<T> item) {
+    final AtomicReference<Provider<T>> ref =
+        new AtomicReference<Provider<T>>(item);
+    items.add(ref);
+    return new RegistrationHandle() {
+      @Override
+      public void remove() {
+        if (ref.compareAndSet(item, null)) {
+          items.remove(ref);
+        }
+      }
+    };
+  }
+
+  /**
+   * Add one new element that may be hot-replaceable in the future.
+   *
+   * @param key unique description from the item's Guice binding. This can be
+   *        later obtained from the registration handle to facilitate matching
+   *        with the new equivalent instance during a hot reload.
+   * @param item the item to add to the collection right now. Must not be null.
+   * @return a handle that can remove this item later, or hot-swap the item
+   *         without it ever leaving the collection.
+   */
+  public ReloadableRegistrationHandle<T> add(Key<T> key, Provider<T> item) {
+    AtomicReference<Provider<T>> ref = new AtomicReference<Provider<T>>(item);
+    items.add(ref);
+    return new ReloadableHandle(ref, key, item);
+  }
+
+  private class ReloadableHandle implements ReloadableRegistrationHandle<T> {
+    private final AtomicReference<Provider<T>> ref;
+    private final Key<T> key;
+    private final Provider<T> item;
+
+    ReloadableHandle(AtomicReference<Provider<T>> ref,
+        Key<T> key,
+        Provider<T> item) {
+      this.ref = ref;
+      this.key = key;
+      this.item = item;
+    }
+
+    @Override
+    public void remove() {
+      if (ref.compareAndSet(item, null)) {
+        items.remove(ref);
+      }
+    }
+
+    @Override
+    public Key<T> getKey() {
+      return key;
+    }
+
+    @Override
+    public ReloadableHandle replace(Key<T> newKey, Provider<T> newItem) {
+      if (ref.compareAndSet(item, newItem)) {
+        return new ReloadableHandle(ref, newKey, newItem);
+      }
+      return null;
+    }
+  }
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java
new file mode 100644
index 0000000..6c21553
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java
@@ -0,0 +1,65 @@
+// 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.extensions.registration;
+
+import com.google.inject.Binding;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.internal.UniqueAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+class DynamicSetProvider<T> implements Provider<DynamicSet<T>> {
+  private static final Class<?> UNIQUE_ANNOTATION =
+      UniqueAnnotations.create().getClass();
+  private final TypeLiteral<T> type;
+
+  @Inject
+  private Injector injector;
+
+  DynamicSetProvider(TypeLiteral<T> type) {
+    this.type = type;
+  }
+
+  public DynamicSet<T> get() {
+    return new DynamicSet<T>(find(injector, type));
+  }
+
+  private static <T> List<AtomicReference<Provider<T>>> find(
+      Injector src,
+      TypeLiteral<T> type) {
+    List<Binding<T>> bindings = src.findBindingsByType(type);
+    int cnt = bindings != null ? bindings.size() : 0;
+    if (cnt == 0) {
+      return Collections.emptyList();
+    }
+    List<AtomicReference<Provider<T>>> r = newList(cnt);
+    for (Binding<T> b : bindings) {
+      if (b.getKey().getAnnotation() != null) {
+        r.add(new AtomicReference<Provider<T>>(b.getProvider()));
+      }
+    }
+    return r;
+  }
+
+  private static <T> List<AtomicReference<Provider<T>>> newList(int cnt) {
+    return new ArrayList<AtomicReference<Provider<T>>>(cnt);
+  }
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicMapImpl.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicMapImpl.java
new file mode 100644
index 0000000..3558794
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicMapImpl.java
@@ -0,0 +1,97 @@
+// 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.extensions.registration;
+
+import com.google.gerrit.extensions.annotations.Export;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+
+/** <b>DO NOT USE</b> */
+public class PrivateInternals_DynamicMapImpl<T> extends DynamicMap<T> {
+  PrivateInternals_DynamicMapImpl() {
+  }
+
+  /**
+   * Store one new element into the map.
+   *
+   * @param pluginName unique name of the plugin providing the export.
+   * @param exportName name the plugin has exported the item as.
+   * @param item the item to add to the collection. Must not be null.
+   * @return handle to remove the item at a later point in time.
+   */
+  public RegistrationHandle put(
+      String pluginName, String exportName,
+      final Provider<T> item) {
+    final NamePair key = new NamePair(pluginName, exportName);
+    items.put(key, item);
+    return new RegistrationHandle() {
+      @Override
+      public void remove() {
+        items.remove(key, item);
+      }
+    };
+  }
+
+  /**
+   * Store one new element that may be hot-replaceable in the future.
+   *
+   * @param pluginName unique name of the plugin providing the export.
+   * @param key unique description from the item's Guice binding. This can be
+   *        later obtained from the registration handle to facilitate matching
+   *        with the new equivalent instance during a hot reload. The key must
+   *        use an {@link @Export} annotation.
+   * @param item the item to add to the collection right now. Must not be null.
+   * @return a handle that can remove this item later, or hot-swap the item
+   *         without it ever leaving the collection.
+   */
+  public ReloadableRegistrationHandle<T> put(
+      String pluginName, Key<T> key,
+      Provider<T> item) {
+    String exportName = ((Export) key.getAnnotation()).value();
+    NamePair np = new NamePair(pluginName, exportName);
+    items.put(np, item);
+    return new ReloadableHandle(np, key, item);
+  }
+
+  private class ReloadableHandle implements ReloadableRegistrationHandle<T> {
+    private final NamePair np;
+    private final Key<T> key;
+    private final Provider<T> item;
+
+    ReloadableHandle(NamePair np, Key<T> key, Provider<T> item) {
+      this.np = np;
+      this.key = key;
+      this.item = item;
+    }
+
+    @Override
+    public void remove() {
+      items.remove(np, item);
+    }
+
+    @Override
+    public Key<T> getKey() {
+      return key;
+    }
+
+    @Override
+    public ReloadableHandle replace(Key<T> newKey, Provider<T> newItem) {
+      if (items.replace(np, item, newItem)) {
+        return new ReloadableHandle(np, newKey, newItem);
+      }
+      return null;
+    }
+  }
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicTypes.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicTypes.java
new file mode 100644
index 0000000..66dd45d
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicTypes.java
@@ -0,0 +1,175 @@
+// 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.extensions.registration;
+
+import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.inject.Binding;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+
+import java.lang.reflect.ParameterizedType;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** <b>DO NOT USE</b> */
+public class PrivateInternals_DynamicTypes {
+  public static Map<TypeLiteral<?>, DynamicSet<?>> dynamicSetsOf(Injector src) {
+    Map<TypeLiteral<?>, DynamicSet<?>> m = newHashMap();
+    for (Map.Entry<Key<?>, Binding<?>> e : src.getBindings().entrySet()) {
+      TypeLiteral<?> type = e.getKey().getTypeLiteral();
+      if (type.getRawType() == DynamicSet.class) {
+        ParameterizedType p = (ParameterizedType) type.getType();
+        m.put(TypeLiteral.get(p.getActualTypeArguments()[0]),
+            (DynamicSet<?>) e.getValue().getProvider().get());
+      }
+    }
+    if (m.isEmpty()) {
+      return Collections.emptyMap();
+    }
+    return Collections.unmodifiableMap(m);
+  }
+
+  public static Map<TypeLiteral<?>, DynamicMap<?>> dynamicMapsOf(Injector src) {
+    Map<TypeLiteral<?>, DynamicMap<?>> m = newHashMap();
+    for (Map.Entry<Key<?>, Binding<?>> e : src.getBindings().entrySet()) {
+      TypeLiteral<?> type = e.getKey().getTypeLiteral();
+      if (type.getRawType() == DynamicMap.class) {
+        ParameterizedType p = (ParameterizedType) type.getType();
+        m.put(TypeLiteral.get(p.getActualTypeArguments()[0]),
+            (DynamicMap<?>) e.getValue().getProvider().get());
+      }
+    }
+    if (m.isEmpty()) {
+      return Collections.emptyMap();
+    }
+    return Collections.unmodifiableMap(m);
+  }
+
+  public static List<RegistrationHandle> attachSets(
+      Injector src,
+      Map<TypeLiteral<?>, DynamicSet<?>> sets) {
+    if (src == null || sets == null || sets.isEmpty()) {
+      return Collections.emptyList();
+    }
+
+    List<RegistrationHandle> handles = new ArrayList<RegistrationHandle>(4);
+    try {
+      for (Map.Entry<TypeLiteral<?>, DynamicSet<?>> e : sets.entrySet()) {
+        @SuppressWarnings("unchecked")
+        TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
+
+        @SuppressWarnings("unchecked")
+        DynamicSet<Object> set = (DynamicSet<Object>) e.getValue();
+
+        for (Binding<Object> b : bindings(src, type)) {
+          if (b.getKey().getAnnotation() != null) {
+            handles.add(set.add(b.getKey(), b.getProvider()));
+          }
+        }
+      }
+    } catch (RuntimeException e) {
+      remove(handles);
+      throw e;
+    } catch (Error e) {
+      remove(handles);
+      throw e;
+    }
+    return handles;
+  }
+
+  public static List<RegistrationHandle> attachMaps(
+      Injector src,
+      String groupName,
+      Map<TypeLiteral<?>, DynamicMap<?>> maps) {
+    if (src == null || maps == null || maps.isEmpty()) {
+      return Collections.emptyList();
+    }
+
+    List<RegistrationHandle> handles = new ArrayList<RegistrationHandle>(4);
+    try {
+      for (Map.Entry<TypeLiteral<?>, DynamicMap<?>> e : maps.entrySet()) {
+        @SuppressWarnings("unchecked")
+        TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
+
+        @SuppressWarnings("unchecked")
+        PrivateInternals_DynamicMapImpl<Object> set =
+            (PrivateInternals_DynamicMapImpl<Object>) e.getValue();
+
+        for (Binding<Object> b : bindings(src, type)) {
+          if (b.getKey().getAnnotation() != null) {
+            handles.add(set.put(groupName, b.getKey(), b.getProvider()));
+          }
+        }
+      }
+    } catch (RuntimeException e) {
+      remove(handles);
+      throw e;
+    } catch (Error e) {
+      remove(handles);
+      throw e;
+    }
+    return handles;
+  }
+
+  public static LifecycleListener registerInParentInjectors() {
+    return new LifecycleListener() {
+      private List<RegistrationHandle> handles;
+
+      @Inject
+      private Injector self;
+
+      @Override
+      public void start() {
+        handles = new ArrayList<RegistrationHandle>(4);
+        Injector parent = self.getParent();
+        while (parent != null) {
+          handles.addAll(attachSets(self, dynamicSetsOf(parent)));
+          handles.addAll(attachMaps(self, "gerrit", dynamicMapsOf(parent)));
+          parent = parent.getParent();
+        }
+        if (handles.isEmpty()) {
+          handles = null;
+        }
+      }
+
+      @Override
+      public void stop() {
+        remove(handles);
+        handles = null;
+      }
+    };
+  }
+
+  private static void remove(List<RegistrationHandle> handles) {
+    if (handles != null) {
+      for (RegistrationHandle handle : handles) {
+        handle.remove();
+      }
+    }
+  }
+
+  private static <K,V> Map<K, V> newHashMap() {
+    return new HashMap<K,V>();
+  }
+
+  private static <T> List<Binding<T>> bindings(Injector src, TypeLiteral<T> type) {
+    return src.findBindingsByType(type);
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/RegistrationHandle.java
similarity index 69%
copy from gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java
copy to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/RegistrationHandle.java
index 3370b08..2243786 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/RegistrationHandle.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 The Android Open Source Project
+// 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.
@@ -12,8 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.cache;
+package com.google.gerrit.extensions.registration;
 
-public interface CachePool {
-  public <K, V> ProxyCache<K, V> register(CacheProvider<K, V> provider);
+/** Handle for registered information. */
+public interface RegistrationHandle {
+  /** Delete this registration. */
+  public void remove();
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeRestoreEvent.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/ReloadableRegistrationHandle.java
similarity index 61%
copy from gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeRestoreEvent.java
copy to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/ReloadableRegistrationHandle.java
index 1a2922b..7284296 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeRestoreEvent.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/ReloadableRegistrationHandle.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 The Android Open Source Project
+// 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.
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.events;
+package com.google.gerrit.extensions.registration;
 
-public class ChangeRestoreEvent extends ChangeEvent {
-    public final String type = "change-restored";
-    public ChangeAttribute change;
-    public PatchSetAttribute patchSet;
-    public AccountAttribute restorer;
-    public String reason;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+
+public interface ReloadableRegistrationHandle<T> extends RegistrationHandle {
+  public Key<T> getKey();
+
+  public RegistrationHandle replace(Key<T> key, Provider<T> item);
 }
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
new file mode 100644
index 0000000..3d2df21
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/systemstatus/ServerInformation.java
@@ -0,0 +1,42 @@
+// 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.extensions.systemstatus;
+
+/** Exports current server information to an extension. */
+public interface ServerInformation {
+  /** Current state of the server. */
+  public enum State {
+    /**
+     * The server is starting up, and network connections are not yet being
+     * accepted. Plugins or extensions starting during this time are starting
+     * for the first time in this process.
+     */
+    STARTUP,
+
+    /**
+     * The server is running and handling requests. Plugins starting during this
+     * state may be reloading, or being installed into a running system.
+     */
+    RUNNING,
+
+    /**
+     * The server is attempting a graceful halt of operations and will exit (or
+     * be killed by the operating system) soon.
+     */
+    SHUTDOWN;
+  }
+
+  State getState();
+}