Merge "User upload documentation: Replace changes"
diff --git a/Documentation/Makefile b/Documentation/Makefile
index e7f4ee02..5522239 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -75,7 +75,6 @@
@rm -f $@+ $@
@$(ASCIIDOC) -a toc \
-a data-uri \
- -a icons \
-a 'revision=$(REVISION)' \
-b xhtml11 \
-f asciidoc.conf \
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index f6cc5ba..e7379ea 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -551,7 +551,7 @@
Ownership over a particular branch subspace may be delegated by
entering a branch pattern. To delegate control over all branches
that begin with `qa/` to the QA group, add `Owner` category
-for reference `refs/heads/qa/*`. Members of the QA group can
+for reference `refs/heads/qa/\*`. Members of the QA group can
further refine access, but only for references that begin with
`refs/heads/qa/`. See <<project_owners,project owners>> to find
out more about this role.
@@ -619,15 +619,15 @@
[[category_push_merge]]
-Upload Merge Commits
+Push Merge Commits
~~~~~~~~~~~~~~~~~~~~
-The `Push Merge` permits the user to upload merge commits. It's an
-addon to the `Push` permission, and so it won't be sufficient
-with only `Push Merge` granted for a push to happen. Some projects
-wish to restrict merges to being created by Gerrit. By granting
-`Push` without `Push Merge`, the only merges that enter the
-system will be those created by Gerrit.
+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
+merges that enter the system will be those created by Gerrit.
[[category_push_annotated]]
@@ -787,24 +787,26 @@
[options="header"]
|=================================================================================
|Gerrit 2.1.x |Gerrit 2.2.x
-|Code review |Label: Code review
-|Verify |Label: Verify
-|Forge Identity +1 |Forge author identity
-|Forge Identity +2 |Forge committer & author identity
-|Forge Identity +3 |Forge server & committer & author identity
-|Owner |Owner
-|Push branch +1 |Push
-|Push branch +2 |Create reference & Push
-|Push branch +3 |Push (with force) & Create reference
+|Code review |<<category_label-Code-Review,Label: Code review>>
+|Verify |<<category_label-Verified,Label: Verify>>
+|Forge Identity +1 |Forge <<category_forge_author,author>> identity
+|Forge Identity +2 |Forge <<category_forge_committer,committer>> & <<category_forge_author,author>> identity
+|Forge Identity +3 |Forge <<category_forge_server,server>> & <<category_forge_committer,committer>> & <<category_forge_author,author>> identity
+|Owner |<<category_owner,Owner>>
+|Push branch +1 |<<category_push_direct,Push>>
+|Push branch +2 |<<category_create,Create reference>> & <<category_push_direct,Push>>
+|Push branch +3 |<<category_push_direct,Push>> (with force) & <<category_create,Create reference>>
|Push tag +1 & Push Branch +2 |No support to limit to push signed tag
-|Push tag +2 & Push Branch +2 |Push annotated tag
-|Push tag +3 & Push Branch +2 |Create reference
-|Read +1 |Read
-|Read +2 |Read & Push branch (refs/for/refs/...)
-|Read +3 |Read & Push branch (refs/for/refs/...) & Push merge
-|Submit |Submit
+|Push tag +2 & Push Branch +2 |<<category_push_annotated,Push annotated tag>>
+|Push Branch +2 (refs/tags/*) |<<category_create,Create reference>> (refs/tags/...)
+|Push Branch +3 (refs/tags/*) |<<category_push_direct,Push>> (with force on refs/tags/...)
+|Read +1 |<<category_read,Read>>
+|Read +2 |<<category_read,Read>> & <<category_push_review,Push>> (refs/for/refs/...)
+|Read +3 |<<category_read,Read>> & <<category_push_review,Push>> (refs/for/refs/...) & <<category_push_merge,Push Merge Commit>>
+|Submit |<<category_submit,Submit>>
|=================================================================================
+
[NOTE]
In Gerrit 2.2.x, the way to set permissions for upload has changed entirely.
To upload a change for review is no longer a separate permission type,
@@ -813,6 +815,80 @@
on `refs/for/refs/heads/<branch>` rather than permissions to upload changes
on `refs/heads/<branch>`.
+
+System capabilities
+-------------------
+
+The system capabilities control actions that the administrators of
+the server can perform which usually affect the entire
+server in some way. The administrators may delegate these
+capabilities to trusted groups of users.
+
+Delegation of capabilities allows groups to be granted a subset of
+administrative capabilities without being given complete
+administrative control of the server. This makes it possible to
+keep fewer users in the administrators group, even while spreading
+much of the server administration burden out to more users.
+
+Below you find a list of capabilities available:
+
+
+* Create Group
+
+* Create Project
+
+* Flush Caches
+
+* Kill Task
+
+* Priority
+
+* Start Replication
+
+* View Caches
+
+* View Connections
+
+* View Queue
+
+
+[[capability_administrateServer]]
+Administrate Server
+~~~~~~~~~~~~~~~~~~~
+
+This is in effect the owner and administrator role of the Gerrit
+instance. Any members of a group granted this capability will be
+able to grant any access right to any group. They will also have all
+capabilities granted to them automatically.
+
+
+[[capability_createAccount]]
+Create Account
+~~~~~~~~~~~~~~
+
+Allow link:cmd-create-account.html['account creation over the ssh prompt'].
+This capability allows the granted group members to create non-interactive
+service accounts. These service accounts are generally used for automation
+and made to be members of the
+link:access-control.html#non-interactive_users['Non-Interactive users'] group.
+
+
+[[capability_queryLimit]]
+Query Limit
+~~~~~~~~~~~
+
+Allow site administrators to configure the query limit for users to
+be above the default hard-coded value of 500. Administrators can add
+a global block to `All-Projects` with group(s) that
+should have different limits:
+
+When applying a query limit to a user the largest value granted by
+any of their groups is used.
+
+This limit applies not only to the link:cmd-query.html[`gerrit query`]
+command, but also to the web UI results pagination size.
+
+
[[restart_changes]]
[NOTE]
Restart the Gerrit web application and reload all browsers after
diff --git a/Documentation/cmd-create-group.txt b/Documentation/cmd-create-group.txt
index 1a6c168..7549c38 100644
--- a/Documentation/cmd-create-group.txt
+++ b/Documentation/cmd-create-group.txt
@@ -13,6 +13,7 @@
[--description <DESC>]
[--member <USERNAME>]
[--group <GROUP>]
+ [--visible-to-all]
<GROUP>
DESCRIPTION
@@ -56,6 +57,9 @@
Group name to include in the group. Multiple --group options may
be specified to include more initial groups.
+--visible-to-all::
+ If specified, the group members will be visible to all users.
+
EXAMPLES
--------
Create a new account group called `gerritdev` with two initial members
diff --git a/Documentation/cmd-create-project.txt b/Documentation/cmd-create-project.txt
index 9d2b98b..cc7e929 100644
--- a/Documentation/cmd-create-project.txt
+++ b/Documentation/cmd-create-project.txt
@@ -11,6 +11,7 @@
'ssh' -p <port> <host> 'gerrit create-project'
[--owner <GROUP> ... | -o <GROUP> ...]
[--parent <NAME> | -p <NAME> ]
+ [--suggest-parents | -S ]
[--permissions-only]
[--description <DESC> | -d <DESC>]
[--submit-type <TYPE> | -t <TYPE>]
@@ -75,6 +76,13 @@
through. If not specified, the parent is set to the default
project `All-Projects`.
+--suggest-parents::
+-S::
+ Suggest parent candidates. This option cannot be used with
+ other arguments. Print out a list of projects that are
+ already parents to other projects, thus it can help the user
+ find a suitable parent for the new project.
+
--permissions-only::
Create the project only to serve as a parent for other
projects. The new project's Git repository will be
diff --git a/Documentation/cmd-query.txt b/Documentation/cmd-query.txt
index d5da453..1bd4862 100644
--- a/Documentation/cmd-query.txt
+++ b/Documentation/cmd-query.txt
@@ -64,6 +64,8 @@
--files::
Support for listing files with patch sets and their
attributes (ADDED, MODIFIED, DELETED, RENAMED, COPIED).
+ Note that this option requires either the --current-patch-set
+ or the --patch-sets option in order to give any file information.
--comments::
Include comments for all changes. If combined with the
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index d4e0219..964defd 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -24,6 +24,27 @@
diskbuffer = 10 m
----
+[[accounts]]Section accounts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[[accounts.visibility]]::
++
+Controls visibility of other users' dashboard pages and
+completion suggestions to web users.
++
+If `ALL`, all users are visible to all other users, even
+anonymous users.
++
+If `SAME_GROUP`, only users who are also members of a group the
+current user is a member of are visible.
++
+If `VISIBLE_GROUP`, only users who are members of at least one group
+that is visible to the current user are visible.
++
+If `NONE`, no users other than the current user are visible.
++
+Default is `ALL`.
+
[[addreviewer]]Section addreviewer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -206,7 +227,7 @@
* y, year, years (`1 year` is treated as `365 days`)
+
-Default is 5 days.
+Default is 12 hours.
[[auth.httpHeader]]auth.httpHeader::
+
@@ -592,9 +613,8 @@
+
Default is 5 minutes.
-
[[changeMerge]]Section changeMerge
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Controls whether or not the mergeability test of changes is
enabled. If enabled, when the change page is loaded, the test is
@@ -606,11 +626,10 @@
test = true
----
-+
By default this is false (test is not enabled).
[[commentlink]]Section commentlink
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Comment links are find/replace strings applied to change descriptions,
patch comments, and in-line code comments to turn set strings into
hyperlinks. One common use is for linking to bug-tracking systems.
@@ -1136,7 +1155,7 @@
Valid values are the characters '*', '(' and ')'.
[[hooks]]Section hooks
-~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~
See also link:config-hooks.html[Hooks].
@@ -2014,19 +2033,20 @@
[[suggest.accounts]]::
+
-If `ALL`, all matching user accounts will be offered as
-completion suggestions when adding a reviewer to a change,
-or a user to a group.
+If `true`, visible user accounts (according to the value of
+`accounts.visibility`) will be offered as completion suggestions
+when adding a reviewer to a change, or a user to a group.
+
-If `SAME_GROUP`, only users who are also members of a group the
-current user is a member of will be offered.
+If `false`, account suggestion is disabled.
+
-If `VISIBLE_GROUP`, only users who are members of at least one group
-that is visible to the current user will be offered.
+Older configurations may also have one of the `accounts.visibility`
+values for this field, including `OFF` as a synonym for `NONE`. If
+`accounts.visibility` is also set, that value overrides this one;
+otherwise, this value applies to both `suggest.accounts` and
+`accounts.visibility`.
+
-If `OFF`, no account suggestions are given.
-+
-Default is `ALL`.
+New configurations should prefer the boolean value for this field
+and an enum value for `accounts.visibility`.
[[theme]] Section theme
~~~~~~~~~~~~~~~~~~~~~~~
@@ -2157,7 +2177,7 @@
[[upload]]Section upload
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~
Sets the group of users allowed to execute 'upload-pack' on the
server, 'upload-pack' is what runs on the server during a user's
fetch, clone or repo sync command.
@@ -2206,7 +2226,7 @@
File `etc/secure.config`
--------------------------
+------------------------
The optional file `'$site_path'/etc/secure.config` overrides (or
supplements) the settings supplied by `'$site_path'/etc/gerrit.config`.
The file should be readable only by the daemon process and can be
@@ -2244,6 +2264,16 @@
* link:config-replication.html[Git Replication/Mirroring]
+File `etc/peer_keys`
+--------------------
+
+The optional file `'$site_path'/etc/peer_keys` controls who can
+login as the 'Gerrit Code Review' user, required for the link:cmd-suexec.html[suexec]
+command.
+
+The format is one Base-64 encoded public key per line.
+
+
Database system_config
----------------------
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt
new file mode 100644
index 0000000..2609b05
--- /dev/null
+++ b/Documentation/dev-contributing.txt
@@ -0,0 +1,243 @@
+Gerrit Code Review - Contributing
+=================================
+
+Gerrit is developed as a self-hosting open source project and
+very much welcomes contributions from anyone with a contributor's
+agreement on file with the project.
+
+* https://gerrit-review.googlesource.com/
+
+The Contributor License Agreements:
+
+* https://gerrit-review.googlesource.com/static/cla_individual.html
+* https://gerrit-review.googlesource.com/static/cla_corporate.html
+
+As Gerrit is a code review tool, naturally contributions will
+be reviewed before they will get submitted to the code base. To
+start your contribution, please make a git commit and upload it
+for review to the main Gerrit review server. To help speed up the
+review of your change, review these guidelines before submitting
+your change. You can view the pending Gerrit contributions and
+their statuses here:
+
+* https://gerrit-review.googlesource.com/#/q/status:open+project:gerrit,n,z
+
+Depending on the size of that list it might take a while for
+your change to get reviewed. Naturally there are fewer
+approvers than contributors; so anything that you can do to
+ensure that your contribution will undergo fewer revisions
+will speed up the contribution process. This includes helping
+out reviewing other people's changes to relieve the load from
+the approvers. Even if you are not familiar with Gerrit's
+internals, it would be of great help if you can download, try
+out, and comment on new features. If it works as advertised,
+say so, and if you have the priviliges to do so, go ahead
+and give it a +1 Verified. If you would find the feature
+useful, say so and give it a +1 code review.
+
+And finally, the quicker you respond to the comments of your
+reviewers, the quicker your change might get merged! Try to
+reply to every comment after submitting your new patch,
+particularly if you decided against making the suggested change.
+Reviewers don't want to seem like nags and pester you if you
+haven't replied or made a fix, so it helps them know if you
+missed it or decided against it.
+
+
+Review Criteria
+---------------
+
+Here are some hints as to what approvers may be looking for
+before approving or submitting changes to the Gerrit project.
+Let's start with the simple nit picky stuff. You are likely
+excited that your code works; help us share your excitement
+by not distracting us with the simple stuff. Thanks to Gerrit,
+problems are often highlighted and we find it hard to look
+beyond simple spacing issues. Blame it on our short attention
+spans, we really do want your code.
+
+
+Commit Message
+--------------
+
+It is essential to have a good commit message if you want your
+change to be reviewed.
+
+ * Keep lines no longer than 72 chars
+ * Start with a short one line summary
+ * Followed by a blank line
+ * Followed by one or more explanatory paragraphs
+ * Use the present tense (fix instead of fixed)
+ * Include a Bug: Issue <#> line if fixing a Gerrit issue
+ * Include a Change-Id line
+
+
+A sample good Gerrit commit message:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+====
+ Add sample commit message to guidelines doc
+
+ The original patch set for the contributing guidelines doc did not
+ include a sample commit message, this new patchset does. Hopefully this
+ makes things a bit clearer since examples can sometimes help when
+ explanations don't.
+
+ Note that the body of this commit message can be several paragraphs, and
+ that I word wrap it at 72 characters. Also note that I keep the summary
+ line under 50 characters since it is often truncated by tools which
+ display just the git summary.
+
+ Bug: Issue 98765605
+ Change-Id: Ic4a7c07eeb98cdeaf44e9d231a65a51f3fceae52
+====
+
+
+Style
+-----
+
+The basic coding style is covered by the tools/GoogleFormat.xml
+doc, see the link:dev-eclipse.html#Formatting[Eclipse Setup]
+for that.
+
+Highlighted/additional styling notes:
+
+ * It is generally more important to match the style of the nearby
+ code which you are modifying than it is to match the style
+ in the formatting guidelines. This is especially true within the
+ same file.
+ * Review your change in Gerrit to see if it highlights
+ mistakingly deleted/added spaces on lines, trailing spaces.
+ * Line length should be 80 or less, unless the code reads
+ better with something slightly longer. Shorter lines not only
+ help reviewers who may use a tablet to review the code, but future
+ contributors may also like to open several editors side by
+ side while editing new changes.
+ * Use 2 spaces for indent (no tabs)
+ * Use brackets in all ifs, spaces before/after if parens.
+ * Use /** */ style Javadocs for variables.
+
+Additionally, you will notice that most of the newline spacing
+is fairly consistent throughout the code in Gerrit, it helps to
+stick to the blank line conventions. Here are some specific
+examples:
+
+ * Keep a blank line between all class and method declarations.
+ * Do not add blank lines at the beginning or end of class/methods.
+ * Put a blank line between external import sources, but not
+ between internal ones.
+
+
+Code Organization
+-----------------
+
+Do your best to organize classes and methods in a logical way.
+Here are some guidelines that Gerrit uses:
+
+ * Ensure a standard copyright header is included at the top
+ of any new files (copy it from another file, update the year).
+ * Always place loggers first in your class!
+ * Define any static interfaces next in your class.
+ * Define non static interfaces after static interfaces in your
+ class.
+ * Next you should define static types and members.
+ * Finally instance members, then constuctors, and then instance
+ methods.
+ * Some common exceptions are private helper static methods which
+ might appear near the instance methods which they help.
+ * Getters and setters for the same instance field should usually
+ be near each other baring a good reason not to.
+ * If you are using assisted injection, the factory for your class
+ 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
+ all lowercase, which means classes come before packages at the
+ same level).
+
+Wow that's a lot! But don't worry, you'll get the habit and most
+of the code is organized this way already; so if you pay attention
+to the class you are editing you will likely pick up on it.
+Naturally new classes are a little harder; you may want to come
+back and consult this section when creating them.
+
+
+Design
+------
+
+Here are some design level ojectives that you should keep in mind
+when coding:
+
+ * ORM entity objects should match exactly one row in the database.
+ * Most client pages should perform only one RPC to load so as to
+ keep latencies down. Exceptions would apply to RPCs which need
+ to load large data sets if splitting them out will help the
+ page load faster. Generally page loads are expected to complete
+ in under 100ms. This will be the case for most operations,
+ unless the data being fetched is not using Gerrit's caching
+ infrastructure. In these slower cases, it is worth considering
+ mitigating this longer load by using a second RPC to fill in
+ this data after the page is displayed (or alternatively it might
+ be worth proposing caching this data).
+ * @Inject should be used on constructors, not on fields. The
+ current exceptions are the ssh commands, these were implemented
+ earlier in Gerrit's development. To stay consistent, new ssh
+ commands should follow this older pattern; but eventually these
+ should get converted to eliminate this exception.
+ * Don't leave repository objects (git or schema) open. A .close()
+ after every open should be placed in a finally{} block.
+ * Don't leave UI components, which can cause new actions to occur,
+ enabled during RPCs which update the DB. This is to prevent
+ people from submitting actions more than once when operating
+ 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.
+
+
+Tests
+-----
+
+ * Tests for new code will greatly help your change get approved.
+
+
+Change Size/Number of Files Touched
+-----------------------------------
+
+And finally, I probably cannot say enough about change sizes.
+Generally, smaller is better, hopefully within reason. Do try to
+keep things which will be confusing on their own together,
+especially if changing one without the other will break something!
+
+ * If a new feature is implemented and it is a larger one, try to
+ identify if it can be split into smaller logical features; when
+ in doubt, err on the smaller side.
+ * Separate bug fixes from feature improvements. The bug fix may
+ be an easy candidate for approval and should not need to wait
+ for new features to be approved. Also, combining the two makes
+ reviewing harder since then there is no clear line between the
+ fix and the feature.
+ * Separate supporting refactoring from feature changes. If your
+ new feature requires some refactoring, it helps to make the
+ refactoring a separate change which your feature change
+ depends on. This way, reviewers can easily review the refactor
+ change as a something that should not alter the current
+ functionality, and feel more confident they can more easily
+ spot errors this way. Of course, it also makes it easier to
+ test and locate later on if an unfortunate error does slip in.
+ Lastly, by not having to see refactoring changes at the same
+ time, it helps reviewers understand how your feature changes
+ the current functionality.
+ * Separate logical features into separate changes. This
+ is often the hardest part. Here is an example: when adding a
+ new ability, make separate changes for the UI and the ssh
+ commands if possible.
+ * Do only what the commit message describes. In other words, things which
+ are not strictly related to the commit message shouldn't be part of
+ a change, even trivial things like externalizing a string somewhere
+ or fixing a typo. This help keep "git blame" more useful in the future
+ and it also makes "git revert" more useful.
+ * Use topic branches to link your separate changes together.
+
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt
index 82d0a67..b37223c 100644
--- a/Documentation/dev-eclipse.txt
+++ b/Documentation/dev-eclipse.txt
@@ -16,6 +16,7 @@
http://m2eclipse.codehaus.org/[m2eclipse]
+[[Formatting]]
Code Formatter Settings
-----------------------
diff --git a/Documentation/dev-readme.txt b/Documentation/dev-readme.txt
index 2490695..552b5a8 100644
--- a/Documentation/dev-readme.txt
+++ b/Documentation/dev-readme.txt
@@ -11,7 +11,7 @@
Create a new client workspace:
----
- git clone https://code.google.com/p/gerrit
+ git clone https://gerrit.googlesource.com/gerrit
cd gerrit
----
diff --git a/Documentation/error-not-allowed-to-upload-merges.txt b/Documentation/error-not-allowed-to-upload-merges.txt
index 9aa144a..981ba91c 100644
--- a/Documentation/error-not-allowed-to-upload-merges.txt
+++ b/Documentation/error-not-allowed-to-upload-merges.txt
@@ -7,9 +7,8 @@
If you need to upload merge commits, you can contact one of the
project owners and request permissions to upload merge commits
-(access right link:access-control.html#category_push['Push'] and
-link:access-control.html#category_push_merge['Push merge']) for this
-project.
+(access right link:access-control.html#category_push_merge['Push Merge Commit'])
+for this project.
If one of your changes could not be merged in Gerrit due to conflicts
and you created the merge commit to resolve the conflicts, you might
diff --git a/Documentation/error-prohibited-by-gerrit.txt b/Documentation/error-prohibited-by-gerrit.txt
index 7adbe8e..69f80c1 100644
--- a/Documentation/error-prohibited-by-gerrit.txt
+++ b/Documentation/error-prohibited-by-gerrit.txt
@@ -12,10 +12,11 @@
link:access-control.html#category_push_review['Push'] on
`refs/for/refs/heads/*`)
2. if you bypass code review without
- link:access-control.html#category_push_direct['Push'] privileges
+ link:access-control.html#category_push_direct['Push'] access right
on `refs/heads/*`
-3. if you push a signed or annotated tag without
- link:access-control.html#category_pTAG['Push Tag'] privileges
+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
Reference'] for the reference name 'refs/tags/*'
diff --git a/Documentation/index.txt b/Documentation/index.txt
index b86d4b3..5143bf7 100644
--- a/Documentation/index.txt
+++ b/Documentation/index.txt
@@ -18,6 +18,7 @@
* link:user-signedoffby.html[Signed-off-by Lines]
* link:access-control.html[Access Controls]
* link:error-messages.html[Error Messages]
+* link:user-submodules.html[Subscribing to Git Submodules]
Installation
------------
@@ -45,6 +46,7 @@
* link:dev-readme.html[Developer Setup]
* link:dev-eclipse.html[Eclipse Setup]
+* link:dev-contributing.html[Contributing to Gerrit]
* link:dev-design.html[System Design]
* link:i18n-readme.html[i18n Support]
diff --git a/Documentation/install-quick.txt b/Documentation/install-quick.txt
index 73e5f55..6bea7f8 100644
--- a/Documentation/install-quick.txt
+++ b/Documentation/install-quick.txt
@@ -30,7 +30,7 @@
$ java -version
java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03-384-10M3425)
- Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02-384, mixed mode)
+ Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02-384, mixed mode)
----
If Java isn't installed, get it:
@@ -46,8 +46,8 @@
First create the user and then become the user:
----
- sudo adduser gerrit2
- sudo su gerrit2
+ $ sudo adduser gerrit2
+ $ sudo su gerrit2
----
If you don't have root privileges you could skip this step and run gerrit
@@ -64,9 +64,9 @@
* http://code.google.com/p/gerrit/downloads/list[A list of releases available]
-This tutorial is based on version 2.2.1, and you can download that from this link
+This tutorial is based on version 2.2.2, and you can download that from this link
-* http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.1.war[Link to the 2.2.1 war archive]
+* http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.2.war[Link to the 2.2.2 war archive]
[[initialization]]
@@ -76,27 +76,25 @@
It's time to run the initialization, and with the batch switch enabled, we don't have to answer any questions at all:
----
-
-gerrit2@host:~$ java -jar gerrit.war init --batch -d ~/gerrit_testsite
-Generating SSH host key ... rsa(simple)... done
-Initialized /home/gerrit2/gerrit_testsite
-Executing /home/gerrit2/gerrit_testsite/bin/gerrit.sh start
-Starting Gerrit Code Review: OK
-gerrit2@host:~$
-
+ gerrit2@host:~$ java -jar gerrit.war init --batch -d ~/gerrit_testsite
+ Generating SSH host key ... rsa(simple)... done
+ Initialized /home/gerrit2/gerrit_testsite
+ Executing /home/gerrit2/gerrit_testsite/bin/gerrit.sh start
+ Starting Gerrit Code Review: OK
+ gerrit2@host:~$
----
When the init is complete, you can review your settings in the
file `'$site_path/etc/gerrit.config'`.
-Installation is complete and you're ready to start up Gerrit!
+An important setting will be the canonicalWebUrl which will
+be needed later to access gerrit's web interface.
----
-gerrit2@host:~$ $site_path/bin/gerrit.sh start
-Starting Gerrit Code Review: OK
-Waiting for server to start ... OK
+ gerrit2@host:~$ cat ~/gerrit_testsite/etc/gerrit.config | grep canonical
+ canonicalWebUrl = http://localhost:8080/
+ gerrit2@host:~$
----
-
[[usersetup]]
The first user
--------------
@@ -111,8 +109,9 @@
id_rsa and id_rsa.pub.
----
-user@host:~$ ls .ssh
-authorized_keys config id_rsa id_rsa.pub known_hosts
+ user@host:~$ ls .ssh
+ authorized_keys config id_rsa id_rsa.pub known_hosts
+ user@host:~$
----
If you have the files, you may skip the key generating step.
@@ -127,40 +126,39 @@
*They will be overwritten!*
----
-user@host:~$ ssh-keygen -t rsa
-Generating public/private rsa key pair.
-Enter file in which to save the key (/home/user/.ssh/id_rsa):
-Created directory '/home/user/.ssh'.
-Enter passphrase (empty for no passphrase):
-Enter same passphrase again:
-Your identification has been saved in /home/user/.ssh/id_rsa.
-Your public key has been saved in /home/user/.ssh/id_rsa.pub.
-The key fingerprint is:
-00:11:22:00:11:22:00:11:44:00:11:22:00:11:22:99 user@host
-The key's randomart image is:
-+--[ RSA 2048]----+
-| ..+.*=+oo.*E|
-| u.OoB.. . +|
-| ..*. |
-| o |
-| . S .. |
-| |
-| |
-| .. |
-| |
-+-----------------+
-
-user@host:~$
+ user@host:~$ ssh-keygen -t rsa
+ Generating public/private rsa key pair.
+ Enter file in which to save the key (/home/user/.ssh/id_rsa):
+ Created directory '/home/user/.ssh'.
+ Enter passphrase (empty for no passphrase):
+ Enter same passphrase again:
+ Your identification has been saved in /home/user/.ssh/id_rsa.
+ Your public key has been saved in /home/user/.ssh/id_rsa.pub.
+ The key fingerprint is:
+ 00:11:22:00:11:22:00:11:44:00:11:22:00:11:22:99 user@host
+ The key's randomart image is:
+ +--[ RSA 2048]----+
+ | ..+.*=+oo.*E|
+ | u.OoB.. . +|
+ | ..*. |
+ | o |
+ | . S .. |
+ | |
+ | |
+ | .. |
+ | |
+ +-----------------+
+ user@host:~$
----
Registering your key in Gerrit
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Open a browser and enter the canonical url you used above when initializing
-Gerrit.
+Open a browser and enter the canonical url you got before when
+initializing Gerrit.
----
-Canonical URL [http://localhost:8080/]:
+ Canonical URL [http://localhost:8080/]:
----
Register a new account in Gerrit through the web interface with the
@@ -182,8 +180,9 @@
about our new key and can identify us by it.
----
-user@host:~$ cat .ssh/id_rsa.pub
-ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA5E785mWtMckorP5v40PyFeui9T50dKpaGYw67Mlv2J3aGBG3tS0qBQxKEpiV0J4+W0RgQHbWfNqdUYen9bC5VVH/GatYWkpL9TjjUcHzF1rX3Eyv7PHuHLAyd/8Zdv6R3saF+hNpp1JW0BSa7HXzK7iNCVA3kBuBthxeGh3OoFbaXHn1zwwVQw8I5+Lp9OOIY7sJEsM/kW699XDV6z2zlkByNVEp45j+g26x5rCnGS8GJM7A0uHsaWJddO6TiyR6/2SOBF1VtKw49XLTQcmDInFAZzUsAZSDKlfYloPkpA6YdqeG0eJqau+jtzuigydoVj4j9xidcJ9HtxZcJNuraw== user@host
+ user@host:~$ cat .ssh/id_rsa.pub
+ ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA5E785mWtMckorP5v40PyFeui9T50dKpaGYw67Mlv2J3aGBG3tS0qBQxKEpiV0J4+W0RgQHbWfNqdUYen9bC5VVH/GatYWkpL9TjjUcHzF1rX3Eyv7PHuHLAyd/8Zdv6R3saF+hNpp1JW0BSa7HXzK7iNCVA3kBuBthxeGh3OoFbaXHn1zwwVQw8I5+Lp9OOIY7sJEsM/kW699XDV6z2zlkByNVEp45j+g26x5rCnGS8GJM7A0uHsaWJddO6TiyR6/2SOBF1VtKw49XLTQcmDInFAZzUsAZSDKlfYloPkpA6YdqeG0eJqau+jtzuigydoVj4j9xidcJ9HtxZcJNuraw== user@host
+ user@host:~$
----
Copy the string starting with ssh-rsa to your clipboard and then paste it
@@ -193,18 +192,18 @@
Verify that the ssh connection works for you.
----
- user@host:~$ ssh user@localhost -p 29418
- The authenticity of host '[localhost]:29418 ([127.0.0.1]:29418)' can't be established.
- RSA key fingerprint is db:07:3d:c2:94:25:b5:8d:ac:bc:b5:9e:2f:95:5f:4a.
- Are you sure you want to continue connecting (yes/no)? yes
- Warning: Permanently added '[localhost]:29418' (RSA) to the list of known hosts.
+ user@host:~$ ssh user@localhost -p 29418
+ The authenticity of host '[localhost]:29418 ([127.0.0.1]:29418)' can't be established.
+ RSA key fingerprint is db:07:3d:c2:94:25:b5:8d:ac:bc:b5:9e:2f:95:5f:4a.
+ Are you sure you want to continue connecting (yes/no)? yes
+ Warning: Permanently added '[localhost]:29418' (RSA) to the list of known hosts.
**** Welcome to Gerrit Code Review ****
Hi user, you have successfully connected over SSH.
Unfortunately, interactive shells are disabled.
- To clone a hosted Git repository, use:
+ To clone a hosted Git repository, use:
git clone ssh://user@localhost:29418/REPOSITORY_NAME.git
@@ -232,10 +231,10 @@
user@host:~$
----
-This will create a test repository to work with.
+This will create a repository that you could clone to work with.
-Previous project exist
-~~~~~~~~~~~~~~~~~~~~~~
+Already existing project
+~~~~~~~~~~~~~~~~~~~~~~~~
The other alternative is if you already have a git project that you
want to try out Gerrit on.
@@ -246,24 +245,30 @@
user@host:~$
----
+You need to make sure that at least initially your account is granted
+"Create Reference" privileges for the refs/heads/* reference.
+This is done via the web interface in the Admin/Projects/Access page
+that correspond to your project.
+
After that it's time to upload the previous history to the server:
----
user@host:~/my-project$ git push ssh://user@localhost:29418/demo-project *:*
- Counting objects: 2011, done.
- Writing objects: 100% (2011/2011), 456293 bytes, done.
- Total 2011 (delta 0), reused 0 (delta 0)
- * [new branch] master -> master
-
+ Counting objects: 2011, done.
+ Writing objects: 100% (2011/2011), 456293 bytes, done.
+ Total 2011 (delta 0), reused 0 (delta 0)
+ To ssh://user@localhost:29418/demo-project
+ * [new branch] master -> master
+ user@host:~/my-project$
----
-This will create a test repository to work with.
+This will create a repository that you could clone to work with.
My first change
---------------
-Download a local clone of the repository
+Download a local clone of the repository and move into it
----
user@host:~$ git clone ssh://user@host:29418/demo-project
@@ -271,18 +276,20 @@
remote: Counting objects: 2, done
remote: Finding sources: 100% (2/2)
remote: Total 2 (delta 0), reused 0 (delta 0)
- user@host:~$
+ user@host:~$ cd demo-project
+ user@host:~/demo-project$
----
Then make a change to it and upload it as a reviewable change in Gerrit.
----
- date > testfile.txt
- git add testfile.txt
- git commit -m "My pretty test commit"
- [master ff643a5] My pretty test commit
- 1 files changed, 1 insertions(+), 0 deletions(-)
- create mode 100644 testfile.txt
+ user@host:~/demo-project$ date > testfile.txt
+ user@host:~/demo-project$ git add testfile.txt
+ user@host:~/demo-project$ git commit -m "My pretty test commit"
+ [master ff643a5] My pretty test commit
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+ create mode 100644 testfile.txt
+ user@host:~/demo-project$
----
Usually when you push to a remote git, you push to the reference
@@ -291,16 +298,17 @@
This virtual name space is known as /refs/for/<branch>
----
- git push origin HEAD:refs/for/master
- Counting objects: 4, done.
- Writing objects: 100% (3/3), 293 bytes, done.
- Total 3 (delta 0), reused 0 (delta 0)
- remote:
- remote: New Changes:
- remote: http://localhost:8080/1
- remote:
- To ssh://user@localhost:29418/demo-project
- * [new branch] HEAD -> refs/for/master
+ user@host:~/demo-project$ git push origin HEAD:refs/for/master
+ Counting objects: 4, done.
+ Writing objects: 100% (3/3), 293 bytes, done.
+ Total 3 (delta 0), reused 0 (delta 0)
+ remote:
+ remote: New Changes:
+ remote: http://localhost:8080/1
+ remote:
+ To ssh://user@localhost:29418/demo-project
+ * [new branch] HEAD -> refs/for/master
+ user@host:~/demo-project$
----
You should now be able to access your change by browsing to the http URL
diff --git a/Documentation/user-submodules.txt b/Documentation/user-submodules.txt
new file mode 100644
index 0000000..3d14437
--- /dev/null
+++ b/Documentation/user-submodules.txt
@@ -0,0 +1,143 @@
+Gerrit Code Review - Superprojects subscribed to submodules updates
+===================================================================
+
+Description
+-----------
+
+Gerrit supports a custom git superproject feature for tracking submodules.
+This feature is useful for automatic updates on superprojects whenever
+a change is merged on tracked submodules. To take advantage of this
+feature, one should add submodule(s) to a local working copy of a
+superproject, edit the created .gitmodules configuration file to
+have a branch field on each submodule section with the value of the
+submodule branch it is subscribing to, commit the changes, push and
+merge the commit.
+
+When a commit is merged to a project, the commit content is scanned
+to identify if it registers git submodules (if the commit registers
+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
+automatically updates the subscribers to the submodule with a new
+commit having the updated gitlinks.
+
+Git Submodules Overview
+-----------------------
+
+It is a git feature that allows an external repository to be
+attached inside a repository at a specific path. The objective here
+is to provide a brief overview, further details can be found
+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".
+With this feature, one could attach 'a' inside of 'super' repository
+at path 'a' by executing the following command when being inside
+'super':
+=====
+git submodule add ssh://server/a a
+====
+
+Still considering the above example, after its execution notice that
+inside the local repository 'super' the 'a' folder is considered a
+gitlink to the external repository 'a'. Also notice a file called
+.gitmodules is created (it is a config file containing the
+subscription of 'a'). To provide the sha-1 each gitlink points to in
+the external repository, one should use the command:
+====
+git submodule status
+====
+
+In the example provided, if 'a' is updated and 'super' is supposed
+to see the latest sha-1 (considering here 'a' has only the master
+branch), one should then commit the modified gitlink for 'a' in
+the 'super' project. Actually it would not even need to be an
+external update, one could move to 'a' folder (insider 'super'),
+modify its content, commit, then move back to 'super' and
+commit the modified gitlink for 'a'.
+
+Creating a New Subscription
+---------------------------
+
+Defining the Submodule Branch
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This is required because Submodule subscription is actually the
+subscription of a submodule project and one of its branches for
+a branch of a super project.
+
+Since it manages subscriptions in the branch scope, we could have
+a scenario having a project called 'super' having a branch 'integration'
+subscribed to a project called 'a' in branch 'integration', and also
+having the same 'super' project but in branch 'dev' subscribed to the 'a'
+project in a branch called 'local-dev'.
+
+After adding the git submodule to a super project, one should edit
+the .gitmodules file to add a branch field to each submodule
+section which is supposed to be subscribed.
+
+The branch field is not filled by the git submodule command. Its value
+should indicate the branch of a submodule project that when updated
+will trigger automatic update of its registered gitlink.
+
+The branch value could be '.' if the submodule project branch
+has the same name as the destination branch of the commit having
+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
+editing .gitmodules file after adding submodules to a super project,
+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
+to automatically update the superproject.
+
+Detecting and Subscribing Submodules
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Whenever a commit is merged to a project, its content is scanned
+to identify if it registers any submodules (if the commit contains new
+gitlinks and a .gitmodules file with all required info) and if so,
+a new submodule subscription is registered.
+
+Automatic Update of Superprojects
+---------------------------------
+
+After a superproject is subscribed to a submodule, it is not
+required to push/merge commits to this superproject to update the
+gitlink to the submodule.
+
+Whenever a commit is merged in a submodule, its subscribed superproject
+is updated.
+
+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
+creates a new commit on branch 'dev' of 'super' updating the gitlink
+to point to the just merged commit.
+
+Canonical Web Url
+~~~~~~~~~~~~~~~~~
+
+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 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
+add/subscribe submodules.
+
+Removing Subscriptions
+----------------------
+
+If one has added a submodule subscription and drops it, it is
+required to merge a commit updating the subscribed super
+project/branch to remove the gitlink and the submodule section
+of the .gitmodules file.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/user-upload.txt b/Documentation/user-upload.txt
index cd1c30e..8e05e72 100644
--- a/Documentation/user-upload.txt
+++ b/Documentation/user-upload.txt
@@ -280,19 +280,26 @@
* `refs/tags/*`: annotated tag objects pointing to any other type
of Git object can be created.
-To push branches, the link:access-control.html#category_push_direct['Push']
-right must be granted to one (or more) of the user's groups. The
-allowed levels within this category are:
+To push branches, the proper access rights must be configured first.
+Here follows a few examples of how to configure this in Gerrit:
* Update: Any existing branch can be fast-forwarded to a new commit.
This is the safest mode as commits cannot be discarded. Creation
-of new branches is rejected.
-* Create: Implies Update, but also allows creation of a new branch
-if the name does not not already designate an existing branch name.
-* Delete: Implies Create and Update, but also allows an existing
+of new branches is rejected. Can be configured with
+link:access-control.html#category_push_direct['Push'] access.
+* Create: Allows creation of a new branch if the name does not
+already designate an existing branch name. Needs
+link:access-control.html#category_create['Create Reference']
+configured. Please note that once created, this permission doesn't
+grant the right to update the branch with further commits (see above
+for update details).
+* Delete: Implies Update, but also allows an existing
branch to be deleted. Since a force push is effectively a delete
followed by a create, but performed atomically on the server and
logged, this also permits forced push updates to branches.
+To grant this access, configure
+link:access-control.html#category_push_direct['Push'] with the
+'Force' option ticked.
To push annotated tags, the `Push Annotated Tag` project right must
be granted to one (or more) of the user's groups. There is only
diff --git a/ReleaseNotes/ReleaseNotes-2.2.2.1.txt b/ReleaseNotes/ReleaseNotes-2.2.2.1.txt
new file mode 100644
index 0000000..4613787
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.2.2.1.txt
@@ -0,0 +1,53 @@
+Release notes for Gerrit 2.2.2.1
+================================
+
+Gerrit 2.2.2.1 is now available:
+
+link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.2.1.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.2.1.war]
+
+
+There are no schema changes from 2.2.2. However, if upgrading from
+anything but 2.2.2, follow the upgrade procedure in the 2.2.2
+link:ReleaseNotes-2.2.2.html[ReleaseNotes].
+
+
+Bug Fixes
+---------
+* issue 1139 Fix change state in patch set approval if reviewer is added to
+closed change
++
+For the dummy patch set approval that is created when a reviewer is
+added the cached change state is always open, which is incorrect if a
+reviewer is added to a closed change. As a result the closed change will
+appear in the reviewers dashboard in the 'Review Requests' section and will
+stay there forever. Ensure the correct change state is cached in the dummy
+patch set approval when it is created.
+
+* issue 1171 Fix ownerin and reviewerin searches
++
+Update the ownerin and reviewerin searches to use AccountGroup.UUID as
+required by commit e662fb3d4d7d0ad05791b8d2143ac5ce58117335.
+
+* issue 871 Display hash of the cherry-pick merge in comment
++
+After merging a change via cherry-pick, we add the commit's
+hash to the comment. This was accidentally removed in
+commit 14246de3c0f81c06bba8d4530e6bf00e918c11b0
+
+
+Documentation
+-------------
+* Update top level SUBMITTING_PATCHES
++
+This document is out of date, the URLs are from last August.
+Direct readers to the new server.
+
+* Add contributing guideline document
+
+* Documentation: update version references for 2.2.2
++
+Correct wording and instructions to be sure they match what would
+be observed with the indicated version of gerrit.
+Expand instructions when needed to ensure all commands could be
+executed and were successful.
+Indent commands and output based on a run of the instructions
diff --git a/ReleaseNotes/ReleaseNotes-2.2.2.txt b/ReleaseNotes/ReleaseNotes-2.2.2.txt
new file mode 100644
index 0000000..3f1f76f
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.2.2.txt
@@ -0,0 +1,669 @@
+Release notes for Gerrit 2.2.2
+==============================
+
+Gerrit 2.2.2 is now available:
+
+link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.2.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.2.war]
+
+Schema Change
+-------------
+*WARNING:* This release contains schema changes. To upgrade:
+----
+ java -jar gerrit.war init -d site_path
+----
+
+*WARNING:* Upgrading to 2.2.x requires the server be first upgraded
+to 2.1.7 (or a later 2.1.x version), and then to 2.2.x.
+
+New Features
+------------
+
+Prolog
+~~~~~~
+* issue 971 Use Prolog Cafe for ChangeControl.canSubmit()
+
+* Add per-project prolog submit rule files
++
+When loading the prolog environment, now checks refs/meta/config
+branch for a file called rules.pl. If it exists, consult the
+file. Expects a predicate called submit_rule. If no file is found,
+uses the default_submit predicate in common_rules.pl.
+
+* Add inheritance of prolog rules
++
+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
+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.
+
+* Load precompiled prolog rules from jar file
++
+Looks in (site)/cache/rules for a jar file called:
+ rules-(sha1 of rules.pl).jar
+Loads the precompiled prolog rules and uses them instead of
+consulting rules.pl. If the jar does not exist, consults rules.pl.
+If rules.pl does not exist, uses the default submit rules.
+
+* Cmd line tool rulec to compile jar from prolog
++
+Rulec takes rules.pl from the refs/meta/config branch and creates a
+jar file named rules-(sha1 of rules.pl).jar in (sitepath)/cache/rules.
+Generates temporary prolog, java src, and class files which are
+deleted afterwards.
+
+* 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
+file and executing: java -jar gerrit.war prolog-shell
+
+Prolog Predicates
+^^^^^^^^^^^^^^^^^
+* Add Prolog Predicates to check commit messages and edits
++
+commit_message returns the commit message as a symbol.
++
+commit_message_matches takes in a regex pattern and checks it against
+the commit message.
++
+commit_edits takes in a regex pattern for filenames and a regex
+pattern for edits. For all files in a commit that match the filename
+regex. Returns true if the edits in any of those files match the
+edit regex.
+
+* Add Prolog Predicates to expose commit filelist
++
+commit_delta/1,3,4 each takes a regular expression and matches it to
+the path of all the files in the latest patchset of a commit.
+If applicable (changes where the file is renamed or copied), the
+regex is also checked against the old path.
++
+commit_delta/1 returns true if any files match the regex
++
+commit_delta/3 returns the changetype and path, if the changetype is
+renamed, it also returns the old path. If the changetype is rename,
+it returns a delete for oldpath and an add for newpath. If the
+changetype is copy, an add is returned along with newpath.
++
+commit_delta/4 returns the changetype, new path, and old path
+ (if applicable).
+
+* Add Prolog predicates that expose the branch, owner,
+project, and topic of a change, the author and committer of the most
+recent patchset in the change, and who is the current user.
+
+* For user-related predicates, if the user is not a gerrit user, will
+return user(anonymous) or similar. Author and committer predicates
+for commits return user(id), name, and email.
+
+* Make max_with_block/4 public
++
+This is the current rule generally applied to a label function. Make
+it exportable for now until we can come back and clean up the legacy
+approval data code.
+
+Web
+~~~
+
+* Support in Firefox delete key in NpIntTextBox
++
+Pressing the delete key while being in a NpIntTextBox (e.g. in the
+text box for the Tab Width or Columns preference when comparing a
+file) now works in Firefox.
+
+* Make sure special keys work in text fields
++
+There is a bug in gwt 2.1.0 that prevents pressing special keys like
+Enter, Backspace etc. from being properly recognized and so they have no effect.
+
+ChangeScreen
+^^^^^^^^^^^^
+* issue 855 Indicate outdated dependencies on the ChangeScreen
++
+If a change dependency is no longer the latest patchSet for that
+change, mark it OUTDATED in the dependencies table and make
+its row red, and add a warning message to the dependencies
+header, also keep the dependencies disclosure panel open
+even when an outdated dependent change is merged.
+Additionally make the link for dependencies link to the
+exact patchSet of the dependent change.
+
+* issue 881 Allow adding groups as reviewer
++
+On the ChangeScreen it is now possible to add a group as reviewer for
+a change. When a group is added as reviewer the group is resolved and
+all its members are added as reviewers to the change.
+
+* Update approvals in web UI to adapt to rules.pl submit_rule
++
+The UI now shows whatever the results of the submit_rule are, which
+permits the submit_rule to make an ApprovalCategory optional, or to
+make a new label required.
+
+Diff Screen
+^^^^^^^^^^^
+* Add top level menus for a new PatchScreen header
++
+Modify the PatchScreen so that the header contents is selectable
+using top level menus. Allow the header to display the commit
+message, the preferences, the Patch Sets, or the File List.
+
+* Add SideBySide and Unified links to Differences top level menus
++
+These new menu entries allow a user to switch view types easily
+without returning to the ChangeScreen. Also, they double as a
+way to hide the header on the PatchScreen (when clicking on the
+currently displayed type).
+
+* Add user pref to retain PatchScreen Header when changing files
+
+* Flip the orientation of PatchHistory Table
+
+* Remove the 'Change SHA1:' from the PatchScreen title
+
+* Remove scrollbar from Commit Message
+
+* Allow comment editing with single click on line numbers
++
+Make it easier to comment (and now possible on android devices which
+zoom on double click) on a patch by simply clicking on the line number.
+
+* Add a "Save" button to the PatchScriptSettingsPanel
++
+The "Update" button now only updates the display. Addittionally,
+for logged in users, a "Save" button now behaves the way that
+"Update" used to behave for logged in users.
+
+* issue 665 Display merge changes as differences from automatic result
++
+Instead of displaying nothing for a two-parent merge commit, compute
+the automatic merge result and display the difference between the
+automatic result that Git would create, and the actual result that
+was uploaded by the author/committer of the merge.
+
+Groups
+^^^^^^
+* Add menu to AccountGroupScreen
++
+This change introduces a menu in the AccountGroupScreen and
+different screens for subsets of the functionality (similar as it's
+done for the ProjectScreen). Links from other screens to the
+AccountGroupScreen are resolved depending on the group type.
+
+* Display groupUUID on AccountGroupInfoScreen
++
+To assign a privilege to a new group by editing the
+'project.config' file, the new group needs to be added to the
+'groups' file in the 'refs/meta/config' branch which requires
+the UUID of the group to be known.
+
+Project Access
+^^^^^^^^^^^^^^
+* Automatically add new rule when adding new permission
++
+If a new permission was added to a block, immediately create the new
+group entry box and focus it, so the user can assign the permission.
+
+* Only show Exclusive checkbox on reference sections
++
+In the access editor, hide the Exclusive checkbox on the
+Global Capabilities section since it has no inheritance and
+the exclusive bit isn't supported.
+
+* Disable editing after successful save of Access screen
++
+When the access has been successfully modified for a project,
+switch back to the "read-only" view where the widgets are all
+disabled and the Edit button is enabled.
+
+Project Branches
+^^^^^^^^^^^^^^^^
+* Display refs/meta/config branch on ProjectBranchesScreen
++
+The new refs/meta/config branch was not shown in the ProjectBranchesScreen.
+Since refs/meta/config is not just any branch, but has a special
+meaning to Gerrit it is now displayed at the top below HEAD.
+
+* Highlight HEAD and refs/meta/config
++
+Since HEAD and refs/meta/config do not represent ordinary branches,
+highlight their rows with a special style in the ProjectBranchesScreen.
+
+URLs
+^^^^
+* Modernize URLs to be shorter and consistent
++
+Instead of http://site/#change,1234 we now use a slightly more
+common looking http://site/#/c/1234 URL to link to a change.
++
+Files within a patch set are now denoted below the change, as in
+http://site/#/c/1234/1/src/module/foo.c
++
+Also fix the dynamic redirects of http://site/1234
+and http://site/r/deadbeef to jump directly to the corresponding
+change if there is exactly one possible URL.
++
+Entities that have multiple views suffix the URL with ",view-name"
+to indicate which view the user wants to see.
+
+* issue 1018 Accept ~ in linkify() URLs
+
+SSH
+~~~
+* Added a set-reviewers ssh command
+
+* Support removing more than one reviewer at once
++
+This way we can batch delete reviewers from a change.
+
+* issue 881 Support adding groups as reviewer by SSH command
++
+With the set-reviewers SSH command it is now possible to also add
+groups as reviewer for a change.
+
+* Fail review command for changing labels when change is closed
++
+If a reviewer attempts to change a review label (approval) after a
+change is closed using the ssh review command, cause it to fail the
+command and output a message.
+
+* ls-projects: Fix display of All-Projects under --tree
++
+Everything should be nested below All-Projects, since that is actually
+the root level.
+
+* ls-projects: Add --type to filter by project type
++
+ls-projects now supports --type code|permissions|all. The default is
+code which now skips permissions only projects, restoring the output
+to what appears from Gerrit 2.1.7 and earlier.
+
+* show-caches: Improve memory reporting
++
+Change the way memory is reported to show the actual values,
+and the equation that determines how these are put together
+to form the current usage. Include some additional data including
+server version, current time, process uptime, active SSH
+connections, and tasks in the task queue. The --show-jvm option
+will report additional data about the JVM, and tell the caller
+where it is running.
+
+Queries
+^^^^^^^
+* Output patchset creation date for 'query' command.
+
+* issue 1053 Support comments option in query command
++
+Query SSH command will show all comments if option --comments is
+used. If --comments is used together with --patch-sets all inline
+comments are included in the output.
+
+Config
+~~~~~~
+* Move batch user priority to a capability
++
+Instead of using a magical group, use a special capability to
+denote users that should get the batch priority behavior.
+
+* issue 742 Make administrator, create-project a global capability
++
+This gets rid of the special entries in system_config and
+gerrit.config related to who the Administrators group is,
+or which groups are permitted to create new projects on
+this server.
+
+* issue 48 & 742 Add fine-grained capabilities for administrative actions
++
+The Global Capabilities section in All-Projects can now be used to
+grant subcommands that are available over SSH and were previously
+restricted to only Administrators.
+
+* Disallow project names ending in "/"
+
+* issue 934 query: Enable configurable result limit
++
+Allow site administrators to configure the query limit for user to be
+above the default hard-coded value of 500 by adding a global
+[capability] block to All-Projects project.config file with group(s)
+that should have different limits.
+
+* Introduced a new PermissionRule.Action: BLOCK.
++
+Besides already existing ALLOW and DENY actions this change
+introduces the BLOCK action in order to enable blocking some
+permission rules globally.
+
+* issue 813 Use remote.name.replicatePermissions to hide permissions
++
+Administrators can now disable replication of permissions-only
+projects and the per-project refs/meta/config in replication.config
+by setting the replicatePermissions field to false.
+
+* Add a Restored.vm template and use it.
++
+The restore action has been erroneously using the Abandoned.vm
+template. Create a template and sender for the restorecommand.
+
+* sshd.advertisedAddress: specify the displayed SSH host/port
++
+This allows aliases which redirect to gerrit's ssh port (say
+from port 22) to be setup and advertised to users.
+
+Dev
+~~~
+* Updated eclipse settings for 3.7 and m2e 1.0
+
+* Fix build in m2eclipse 1.0
++
+Ignore the antrun and the build-helper-maven-plugin tasks in m2eclipse.
+
+* Make Gerrit with gwt 2.3.0 run in gwtdebug mode
+
+* Fix a number of build warnings that have crept in
+
+* Accept email address automatically
++
+Enable Gerrit to accept email address automatically in
+"DEVELOPMENT_BECOME_ANY_ACCOUNT" mode without a confirmation email.
+
+* Added clickable user names at the BecomeAnyAccountLoginServlet.
++
+The first 5 (by accountId) user names are displayed as clickable
+links. Clicking a user name logs in as this user, speeding up
+switching between different users when using the
+DEVELOPMENT_BECOME_ANY_ACCOUNT authentication type.
+
+Miscellaneous
+~~~~~~~~~~~~~
+* Permit adding reviewers to closed changes
++
+Permit adding a reviewer to closed changes to support post-submit
+discussion threads.
+
+* issue 805 Don't check for multiple change-ids when pushing directly
+to refs/heads.
+
+* Avoid costly findMergedInto during push to refs/for/*
++
+No longer close a change when a commit is pushed to res/for/* and the
+Change-Id in the commit message footer matches another commit on an
+existing branch or tag.
+
+* Allow serving static files in subdirectories
+
+* issue 1019 Normalize OpenID URLs with http:// prefix
++
+No longer violate OpenID 1.1 and 2.0, both of which require
+OpenIDs to be normalized (http:// added).
+
+* Allow container-based authentication for git over http
++
+Gerrit was insisting on DIGEST authentication when doing git over
+http. A new boolean configuration parameter auth.trustContainerAuth
+allows gerrit to be configured to trust the container to do the
+authentication.
+
+* issue 848 Add rpc method for GerritConfig
++
+Exposes what types of reviews are possible via json rpc, so that the
+Eclipse Reviews plugin currently can parse the javascript from a
+gerrit page load.
+
+
+Performance
+-----------
+* Bumped Brics version to 1.11.8
++
+This Brics version fixes a performance issue in some larger Gerrit systems.
+
+* Add permission_sort cache to remember sort orderings
++
+Cache the order AccessSections should be sorted in, making any future
+sorting for the same reference name and same set of section patterns
+cheaper.
+
+* Refactor how permissions are matched by ProjectControl, RefControl
++
+More aggressively cache many of the auth objects at a cost of memory,
+but this should be an improvement in response timse.
+
+* Substantialy speed up pushing changes for review
++
+Pushing a new change for review checks if the change is related to
+the branch it's destined for. It used to do this in a way that
+required a topo-sort of the rev history, and now uses JGit's
+merge-base functionality.
+
+* Add cache for tag advertisements
++
+To make the general case more efficient, introduce a cache called "git_tags".
++
+On a trivial usage of the Linux kernel repository, the average
+running time of the VisibleRefFilter when caches were hot was
+7195.68 ms. With this commit, it is a mere 5.07 milliseconds
+on a hot cache. A reduction of 99% of the running time.
+
+* Don't set lastCheckTime in ProjectState
++
+The lastCheckTime/generation fields are actually a counter that
+is incremented using a background thread. The values don't match
+the system clock, and thus reading System.currentTimeMillis()
+during the construction of ProjectState is a waste of resources.
+
+
+Upgrades
+--------
+* Upgrade to GWT 2.3.0
+* Upgrade to Gson to 1.7.1
+* Upgrade to gwtjsonrpc 1.2.4
+* Upgrade to gwtexpui 1.2.5
+* Upgrade to Jsch 0.1.44-1
+* Upgrade to Brics 1.11.8
+
+
+Bug Fixes
+---------
+* Fix: Issue where Gerrit could not linkify certain URLs
+
+* issue 1015 Fix handling of regex ref patterns in Access panel
++
+regex patterns such as "\^refs/heads/[A-Z]{2,}\-[0-9]\+.\*" were being
+prefixed with "refs/heads/", resulting in invalid reference patterns
+like "refs/heads/^refs/heads/[A-Z]{2,}-[0-9]+.*".
+
+* issue 1002 Check for and disallow pushing of invalid refs/meta/config
++
+If the project.config or groups files are somehow invalid on
+the refs/meta/config branch, or would be made invalid due to
+a bad code review being submitted to this branch, reject the
+user's attempt to push.
+
+* issue 1002 Fix NPE in PermissionRuleEditor when group lacks UUID
++
+If a group does not have an entry in the "groups" table within
+the refs/meta/config branch render the group name as a span,
+without the link instead of crashing the UI.
+
+* issue 972 Filter access section rules to only visible groups
++
+Users who are not the owner of an access section can now only
+see group names and rules for groups which they are a member of,
+are visible to all users, or that they own.
+
+* Correctly handle missing refs/meta/config branch
++
+If the refs/meta/config branch did not exist, getRevision() no longer
+throws an NPE when trying to access the ProjectDetail.
+
+* Allow loading Project Access when there is no refs/meta/config
++
+Enable loading the access screen with a null revision field,
+and on save of any edits require the branch to be new.
+
+* create-project: Fix creation vs. replication order
++
+Create the project on remote mirrors before creating either the
+refs/meta/config or the initial empty branch. This way those can be
+replicated to the remote mirrors once they have been created locally.
+
+* create-project: Bring back --permissions-only flag
++
+If a project is permissions only, assign HEAD to point to
+refs/meta/config. This way the gitweb view of the project
+shows the permissions history by default, and clients that
+clone the project are able to get a detached HEAD pointing
+to the current permission state, rather than an empty
+repository.
+
+* create-project: Fix error reporting when repository exists
++
+If a repository already exists, tell the user it already is
+available, without disclosing the server side path from gerrit.basePath.
+
+* Do not log timeout errors on upload and receive connections
+
+* Only automatically create accounts for LDAP systems
++
+If the account management is LDAP, try to automatically create
+accounts by looking up the data in LDAP. Otherwise fail and reject an
+invalid account reference that was supplied on the command line via
+SSH.
+
+* Add missing RevWalk.reset() after checking merge base
++
+This fixes an exception from RevWalk when trying to push a new
+commit for review.
+
+* issue 1069 Do not send an email on reviews when there is no message.
++
+No longer send an email when reviewing a change via ssh, and
+the change message is blank (when no change message is actually
+added to the review).
+
+* Ignore PartialResultException from LDAP.
++
+This exception occurs when the server isn't following referrals for
+you, and thus the result contains a referral. That happens when
+you're using Active Directory. You almost certainly don't really want
+to follow referrals in AD *anyways*, so just ignore these exceptions,
+so we can still use the actual data.
+
+* issue 518 Fix MySQL counter resets
++
+gwtorm 1.1.5 was patched to leave in the dummy row that incremented
+the counter, ensuring the server will use MAX() + 1 instead of 1 on
+the next increment after restart.
+
+* Don't delete account_id row on MySQL
++
+If the table is an InnoDB table deleting the row after allocation may
+cause the sequence to reset when the server restarts, giving out
+duplicate account_ids later.
+
+
+Documentation
+-------------
+
+New Documents
+~~~~~~~~~~~~~
+* First Cut of Gerrit Walkthrough Introduction documentation.
++
+Add a new document intended to be a complement for the existing
+reference documentation to allow potential users to easily get a
+feel for how Gerrit is used, where it fits and whether it will
+work for them.
+
+* Introducing a quick and dirty setup tutorial
++
+The new document covers quick installation, new project and first
+upload. It contains lots of quoted output, with a demo style to it.
+
+Access Control
+~~~~~~~~~~~~~~
+* Code review
+
+* Conversion table between 2.1 and 2.2
++
+Add a table to ease conversion from 2.1.x. The table tries to address
+the old permissions one by one except for the push tag permission which
+in effect needed two permissions to work properly. This should
+be familiar to the administrator used to the 2.1.x permission model
+however.
+
+* Reformatted text
+
+* Verify
++
+Updated some text in the Per project-section and edited the verified
+section to reflect the current label.
+
+* Capabilities
++
+Adds general information about global capabilities, how the server
+ownership is administered.
+
+* Added non-interactive users
++
+This change adds the non-interactive user group.
+It also adds that groups can be members of other groups.
+The groups are now sorted in alphabetical order.
+
+* Reordering categories
++
+Access categories are now sorted to match drop down box in UI
+
+Other Documentation
+~~~~~~~~~~~~~~~~~~~
+* Added additional information on the install instructions.
++
+The installation instructions presumes much prior knowledge,
+make some of that knowledge less implicit.
+
+* Provides a template to the download example.
++
+Clarifies that the example host must be replaced with proper
+hostname.
+
+* Provided an example on how to abandon a change from
+the command line
+
+* update links from kernel.org to code.google.com
+
+
+* Rename '-- All Projects --' in documentation to 'All-Projects'
+
+* Explain 'Automatically resolve conflicts'
+
+* Update documentation for testing SSH connection
++
+The command output that is shown in the example and the description
+how to set the ssh username were outdated.
+
+* Remove unneeded escape characters from the documentation
++
+The old version of asciidoc required certain characters to be escaped
+with a backslash and when the upgrade to the new version was done all
+those backslashes that were used for escaping became visible.
+
+* Clean up pgm-index
++
+Break out the utilities into their own section, and correct
+some of the item descriptions.
+
+* Update manual project creation instructions
+
+* Update project configuration documentation
++
+Remove the textual reference to obsolete SQL insert statement to
+create new projects.
+
+* Clean up command line documentation, examples
++
+The formatting was pretty wrong after upgrading to a newer version
+of AsciiDoc, so fix up most of the formatting, correct some order
+of commands in the index, and make create-project conform to the
+same format used by create-account and create-group.
+
+* Correct syntax of SQL statement for inserting approval category
diff --git a/ReleaseNotes/index.txt b/ReleaseNotes/index.txt
index 2039222..8d223ad 100644
--- a/ReleaseNotes/index.txt
+++ b/ReleaseNotes/index.txt
@@ -4,6 +4,8 @@
[[2_2]]
Version 2.2.x
-------------
+* link:ReleaseNotes-2.2.2.html[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.0.html[2.2.0]
diff --git a/SUBMITTING_PATCHES b/SUBMITTING_PATCHES
index b4448620..e766ef1 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 ssh://review.source.android.com:29418/tools/gerrit.git HEAD:refs/for/master
+ git push https://gerrit-review.googlesource.com/gerrit HEAD:refs/for/master
Long Version:
@@ -55,23 +55,22 @@
Instead, login to the Gerrit Code Review tool at:
- https://review.source.android.com/
+ https://gerrit-review.googlesource.com/
Ensure you have completed one of the necessary contributor
agreements, providing documentation to the project maintainers that
they have right to redistribute your work under the Apache License:
- https://review.source.android.com/#settings,agreements
+ https://gerrit-review.googlesource.com/#/settings/agreements
-Ensure you have registered one or more SSH public keys, so you can
-push your commits directly over SSH:
+Ensure you have obtained a unique HTTP password to identify yourself:
- https://review.source.android.com/#settings,ssh-keys
+ https://gerrit-review.googlesource.com/#/settings/http-password
-Push your patches over SSH to the review server, possibly through
+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 ssh://review.source.android.com:29418/tools/gerrit.git
+ git config remote.review.url https://google-review.googlesource.com/gerrit
git config remote.review.push HEAD:refs/for/master
git push review
diff --git a/contrib/trivial_rebase.py b/contrib/trivial_rebase.py
index a5123a3..a514b4c 100755
--- a/contrib/trivial_rebase.py
+++ b/contrib/trivial_rebase.py
@@ -93,8 +93,8 @@
Returns a list of approval dicts"""
sql_query = ("\"SELECT value,account_id,category_id FROM patch_set_approvals "
- "WHERE change_id = (SELECT change_id FROM changes WHERE "
- "patch_set_id = %s AND change_key = \'%s\') AND value <> 0\""
+ "WHERE patch_set_id = %s AND change_id = (SELECT change_id FROM "
+ "changes WHERE change_key = \'%s\') AND value <> 0\""
% ((patchset - 1), changeId))
gsql_out = GsqlQuery(sql_query, server, port)
approvals = []
diff --git a/gerrit-antlr/pom.xml b/gerrit-antlr/pom.xml
index 8ee4786..efcc190 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.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-antlr</artifactId>
diff --git a/gerrit-common/pom.xml b/gerrit-common/pom.xml
index 814afad..b4395be 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.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-common</artifactId>
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
index d5865e6..809d462 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
@@ -28,6 +28,7 @@
protected AccountInfoCache accounts;
protected boolean allowsAnonymous;
protected boolean canAbandon;
+ protected boolean canPublish;
protected boolean canRestore;
protected boolean canRevert;
protected boolean canDeleteDraft;
@@ -71,6 +72,14 @@
canAbandon = a;
}
+ public boolean canPublish() {
+ return canPublish;
+ }
+
+ public void setCanPublish(final boolean a) {
+ canPublish = a;
+ }
+
public boolean canRestore() {
return canRestore;
}
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 3744ef0..c9be1d4 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
@@ -44,8 +44,21 @@
@SignInRequired
void deleteDraft(PatchLineComment.Key key, AsyncCallback<VoidResult> callback);
+ /**
+ * Deletes the specified draft patch set. If the draft patch set is the only
+ * patch set of the change, then also the change gets deleted.
+ *
+ * @param psid ID of the draft patch set that should be deleted
+ * @param callback callback to report the result of the draft patch set
+ * deletion operation; if the draft patch set was successfully deleted
+ * {@link AsyncCallback#onSuccess(Object)} is invoked and the change
+ * details are passed as parameter; if the change gets deleted because
+ * the draft patch set that was deleted was the only patch set in the
+ * change, then <code>null</code> is passed as result to
+ * {@link AsyncCallback#onSuccess(Object)}
+ */
@SignInRequired
- void deleteDraftPatchSet(PatchSet.Id psid, AsyncCallback<VoidResult> callback);
+ void deleteDraftPatchSet(PatchSet.Id psid, AsyncCallback<ChangeDetail> callback);
@SignInRequired
void publishComments(PatchSet.Id psid, String message,
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 2d79759..4d10a42 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
@@ -31,6 +31,7 @@
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);
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 068293d..9378526 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
@@ -49,25 +49,74 @@
public static class Error {
public static enum Type {
/** Not permitted to abandon this change. */
- ABANDON_NOT_PERMITTED
+ ABANDON_NOT_PERMITTED,
+
+ /** Not permitted to restore this change. */
+ RESTORE_NOT_PERMITTED,
+
+ /** Not permitted to submit this change. */
+ SUBMIT_NOT_PERMITTED,
+
+ /** Approvals or dependencies are lacking for submission. */
+ SUBMIT_NOT_READY,
+
+ /** Review operation invalid because change is closed. */
+ CHANGE_IS_CLOSED,
+
+ /** Not permitted to publish this draft patch set */
+ PUBLISH_NOT_PERMITTED,
+
+ /** Not permitted to delete this draft patch set */
+ DELETE_NOT_PERMITTED,
+
+ /** Review operation not permitted by rule. */
+ RULE_ERROR,
+
+ /** Review operation invalid because patch set is not a draft. */
+ NOT_A_DRAFT,
+
+ /** Error writing change to git repository */
+ GIT_ERROR
}
protected Type type;
+ protected String message;
protected Error() {
}
public Error(final Type type) {
this.type = type;
+ this.message = null;
+ }
+
+ public Error(final Type type, final String message) {
+ this.type = type;
+ this.message = message;
}
public Type getType() {
return type;
}
+ public String getMessage() {
+ return message;
+ }
+
+ public String getMessageOrType() {
+ if (message != null) {
+ return message;
+ }
+ return "" + type;
+ }
+
@Override
public String toString() {
- return type + "";
+ String ret = type + "";
+ if (message != null) {
+ ret += " " + message;
+ }
+ return ret;
}
}
}
diff --git a/gerrit-ehcache/.gitignore b/gerrit-ehcache/.gitignore
new file mode 100644
index 0000000..903c6c8
--- /dev/null
+++ b/gerrit-ehcache/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-ehcache/.settings/org.eclipse.core.resources.prefs b/gerrit-ehcache/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..82eb859
--- /dev/null
+++ b/gerrit-ehcache/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-ehcache/.settings/org.eclipse.core.runtime.prefs b/gerrit-ehcache/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000..8667cfd
--- /dev/null
+++ b/gerrit-ehcache/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-ehcache/.settings/org.eclipse.jdt.core.prefs b/gerrit-ehcache/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..e89c048
--- /dev/null
+++ b/gerrit-ehcache/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,269 @@
+#Thu Jan 19 12:55:44 PST 2012
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-ehcache/.settings/org.eclipse.jdt.ui.prefs b/gerrit-ehcache/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..d4218a5
--- /dev/null
+++ b/gerrit-ehcache/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-ehcache/pom.xml b/gerrit-ehcache/pom.xml
new file mode 100644
index 0000000..417a978
--- /dev/null
+++ b/gerrit-ehcache/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2010 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.3-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-ehcache</artifactId>
+ <name>Gerrit Code Review - Ehcache Bindings</name>
+
+ <description>
+ Bindings to Ehcache
+ </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>
+ </dependencies>
+</project>
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
new file mode 100644
index 0000000..c25c381
--- /dev/null
+++ b/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/EhcachePoolImpl.java
@@ -0,0 +1,271 @@
+// 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-server/src/main/java/com/google/gerrit/server/cache/PopulatingCache.java b/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/PopulatingCache.java
similarity index 92%
rename from gerrit-server/src/main/java/com/google/gerrit/server/cache/PopulatingCache.java
rename to gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/PopulatingCache.java
index 0822cc0..f5c6c45 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/PopulatingCache.java
+++ b/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/PopulatingCache.java
@@ -12,9 +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.ehcache;
-import static java.util.concurrent.TimeUnit.SECONDS;
+import com.google.gerrit.server.cache.Cache;
+import com.google.gerrit.server.cache.EntryCreator;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
@@ -24,8 +25,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.concurrent.TimeUnit;
-
/**
* A decorator for {@link Cache} which automatically constructs missing entries.
* <p>
@@ -109,12 +108,6 @@
}
@Override
- public long getTimeToLive(final TimeUnit unit) {
- final long maxAge = self.getCacheConfiguration().getTimeToLiveSeconds();
- return unit.convert(maxAge, SECONDS);
- }
-
- @Override
public String toString() {
return "Cache[" + self.getName() + "]";
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/SimpleCache.java b/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/SimpleCache.java
similarity index 86%
rename from gerrit-server/src/main/java/com/google/gerrit/server/cache/SimpleCache.java
rename to gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/SimpleCache.java
index 2283f96..e4428e3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/SimpleCache.java
+++ b/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/SimpleCache.java
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.cache;
+package com.google.gerrit.ehcache;
-import static java.util.concurrent.TimeUnit.SECONDS;
+import com.google.gerrit.server.cache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
@@ -23,8 +23,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.concurrent.TimeUnit;
-
/**
* A fast in-memory and/or on-disk based cache.
*
@@ -77,12 +75,6 @@
}
@Override
- public long getTimeToLive(final TimeUnit unit) {
- final long maxAge = self.getCacheConfiguration().getTimeToLiveSeconds();
- return unit.convert(maxAge, SECONDS);
- }
-
- @Override
public String toString() {
return "Cache[" + self.getName() + "]";
}
diff --git a/gerrit-gwtdebug/pom.xml b/gerrit-gwtdebug/pom.xml
index b3b1083..33b6851 100644
--- a/gerrit-gwtdebug/pom.xml
+++ b/gerrit-gwtdebug/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-gwtdebug</artifactId>
diff --git a/gerrit-gwtui/pom.xml b/gerrit-gwtui/pom.xml
index bb0da11..b09b8e3 100644
--- a/gerrit-gwtui/pom.xml
+++ b/gerrit-gwtui/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-gwtui</artifactId>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ConfirmationDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ConfirmationDialog.java
index c4cb770..36169db 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ConfirmationDialog.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ConfirmationDialog.java
@@ -19,8 +19,9 @@
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.Widget;
import com.google.gwtexpui.globalkey.client.GlobalKey;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
import com.google.gwtexpui.user.client.AutoCenterDialogBox;
public class ConfirmationDialog extends AutoCenterDialogBox {
@@ -28,7 +29,7 @@
private Button cancelButton;
- public ConfirmationDialog(final String dialogTitle, final HTML message,
+ public ConfirmationDialog(final String dialogTitle, final SafeHtml message,
final ConfirmationCallback callback) {
super(/* auto hide */false, /* modal */true);
setGlassEnabled(true);
@@ -59,11 +60,12 @@
buttons.add(cancelButton);
final FlowPanel center = new FlowPanel();
- center.add(message);
+ final Widget msgWidget = message.toBlockWidget();
+ center.add(msgWidget);
center.add(buttons);
add(center);
- message.setWidth("400px");
+ msgWidget.setWidth("400px");
setWidget(center);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
index fdb2cbe..c04167e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
@@ -20,7 +20,6 @@
import com.google.gerrit.client.changes.ChangeListScreen;
import com.google.gerrit.client.patches.PatchScreen;
import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.CommandMenuItem;
import com.google.gerrit.client.ui.LinkMenuBar;
import com.google.gerrit.client.ui.LinkMenuItem;
import com.google.gerrit.client.ui.MorphingTabPanel;
@@ -190,13 +189,7 @@
*/
public static void updateImpl(final String token) {
History.newItem(token, false);
-
- if (historyHooks != null) {
- // Because we blocked firing the event our history hooks won't be
- // informed of the current token. Manually fire the event to them.
- //
- dispatchHistoryHooks(token);
- }
+ dispatchHistoryHooks(token);
}
public static void setQueryString(String query) {
@@ -249,10 +242,7 @@
case HTTP_LDAP:
case CLIENT_SSL_CERT_LDAP:
case CUSTOM_EXTENSION:
- if (token.startsWith("/")) {
- token = token.substring(1);
- }
- Location.assign(selfRedirect("/login/" + token));
+ Location.assign(loginRedirect(token));
break;
case DEVELOPMENT_BECOME_ANY_ACCOUNT:
@@ -270,6 +260,15 @@
}
}
+ private static String loginRedirect(String token) {
+ if (token == null) {
+ token = "";
+ } else if (token.startsWith("/")) {
+ token = token.substring(1);
+ }
+ return selfRedirect("/login/" + token);
+ }
+
private static String selfRedirect(String suffix) {
// Clean up the path. Users seem to like putting extra slashes into the URL
// which can break redirections by misinterpreting at either client or server.
@@ -376,6 +375,7 @@
}
private static ArrayList<JavaScriptObject> historyHooks;
+ private static Anchor signInAnchor;
private static native void initHistoryHooks()
/*-{ $wnd['gerrit_addHistoryHook'] = function(h) { @com.google.gerrit.client.Gerrit::addHistoryHook(Lcom/google/gwt/core/client/JavaScriptObject;)(h); }; }-*/;
@@ -397,9 +397,14 @@
/*-{ hook(url); }-*/;
private static void dispatchHistoryHooks(final String historyToken) {
- final String url = Location.getPath() + "#" + historyToken;
- for (final JavaScriptObject hook : historyHooks) {
- callHistoryHook(hook, url);
+ if (signInAnchor != null) {
+ signInAnchor.setHref(loginRedirect(historyToken));
+ }
+ if (historyHooks != null) {
+ final String url = Location.getPath() + "#" + historyToken;
+ for (final JavaScriptObject hook : historyHooks) {
+ callHistoryHook(hook, url);
+ }
}
}
@@ -464,9 +469,7 @@
final String token = view.getToken();
if (!token.equals(History.getToken())) {
History.newItem(token, false);
- if (historyHooks != null) {
- dispatchHistoryHooks(token);
- }
+ dispatchHistoryHooks(token);
}
if (view instanceof ChangeListScreen) {
@@ -510,15 +513,16 @@
});
JumpKeys.register(body);
- if ("".equals(History.getToken())) {
- if (isSignedIn()) {
- display(PageLinks.MINE);
- } else {
- display(PageLinks.toChangeQuery("status:open"));
- }
- } else {
- display(History.getToken());
+ String token = History.getToken();
+ if (token.isEmpty()) {
+ token = isSignedIn()
+ ? PageLinks.MINE
+ : PageLinks.toChangeQuery("status:open");
}
+ if (signInAnchor != null) {
+ signInAnchor.setHref(loginRedirect(token));
+ }
+ display(token);
}
public static void refreshMenuBar() {
@@ -606,14 +610,8 @@
if (cfg.getRegisterUrl() != null) {
menuRight.add(anchor(C.menuRegister(), cfg.getRegisterUrl()));
}
- CommandMenuItem signIn = new CommandMenuItem(C.menuSignIn(),
- new Command() {
- public void execute() {
- doSignIn(History.getToken());
- }
- });
- signIn.setHref(selfRedirect("/login/"));
- menuRight.addItem(signIn);
+ signInAnchor = anchor(C.menuSignIn(), loginRedirect(History.getToken()));
+ menuRight.add(signInAnchor);
break;
case DEVELOPMENT_BECOME_ANY_ACCOUNT:
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
index 3880f59..c5d5e8c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
@@ -25,7 +25,7 @@
<p>To continue, please sign-in again.</p>
notFoundTitle = Not Found
-notFoundBody = The page you requested was not found.
+notFoundBody = The page you requested was not found, or you do not have permission to view this page.
nameAlreadyUsedBody = The name is already in use.
noSuchAccountTitle = Code Review - Unknown User
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
index e08b03a..c928eb9 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
@@ -48,6 +48,7 @@
String descriptionNotifications();
String buttonSaveGroupOptions();
String suggestedGroupLabel();
+ String parentSuggestions();
String headingGroupUUID();
String headingOwner();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
index 137f32d..f2a8ad6 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
@@ -26,6 +26,7 @@
buttonSaveGroupOptions = Save Group Options
suggestedGroupLabel = group
headingParentProjectName = Rights Inherit From
+parentSuggestions = Parent Suggestion
columnProjectName = Project Name
emailOnlyAuthors = Authors
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateProjectScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateProjectScreen.java
index 5f12e4d..7f2ae07 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateProjectScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateProjectScreen.java
@@ -20,6 +20,7 @@
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.ui.HintTextBox;
import com.google.gerrit.client.ui.ProjectNameSuggestOracle;
+import com.google.gerrit.client.ui.ProjectsTable;
import com.google.gerrit.client.ui.Screen;
import com.google.gerrit.reviewdb.Project;
import com.google.gwt.event.dom.client.ClickEvent;
@@ -28,6 +29,7 @@
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid;
@@ -36,6 +38,8 @@
import com.google.gwtexpui.globalkey.client.NpTextBox;
import com.google.gwtjsonrpc.client.VoidResult;
+import java.util.List;
+
public class CreateProjectScreen extends Screen {
private NpTextBox project;
private Button create;
@@ -43,6 +47,7 @@
private SuggestBox sugestParent;
private CheckBox emptyCommit;
private CheckBox permissionsOnly;
+ private ProjectsTable suggestedParentsTab;
public CreateProjectScreen() {
super();
@@ -59,7 +64,6 @@
protected void onInitUI() {
super.onInitUI();
setPageTitle(Util.C.createProjectTitle());
-
addCreateProjectPanel();
}
@@ -78,7 +82,12 @@
fp.add(emptyCommit);
fp.add(permissionsOnly);
fp.add(create);
- add(fp);
+ VerticalPanel vp=new VerticalPanel();
+ vp.add(fp);
+ initSuggestedParents();
+ vp.add(suggestedParentsTab);
+ add(vp);
+
}
private void initCreateTxt() {
@@ -111,6 +120,44 @@
parent.setVisibleLength(50);
}
+ private void initSuggestedParents() {
+ suggestedParentsTab = new ProjectsTable() {
+ {
+ table.setText(0, 1, Util.C.parentSuggestions());
+ }
+
+ @Override
+ protected void populate(final int row, final Project k) {
+ final Anchor projectLink = new Anchor(k.getName());
+ projectLink.addClickHandler(new ClickHandler() {
+
+ @Override
+ public void onClick(ClickEvent event) {
+ sugestParent.setText(getRowItem(row).getName());
+ }
+ });
+
+ table.setWidget(row, 1, projectLink);
+ table.setText(row, 2, k.getDescription());
+
+ setRowItem(row, k);
+ }
+ };
+ suggestedParentsTab.setVisible(false);
+
+ Util.PROJECT_SVC
+ .suggestParentCandidates(new GerritCallback<List<Project>>() {
+ @Override
+ public void onSuccess(List<Project> result) {
+ if (result != null && !result.isEmpty()) {
+ suggestedParentsTab.setVisible(true);
+ suggestedParentsTab.display(result);
+ suggestedParentsTab.finishDisplay();
+ }
+ }
+ });
+ }
+
private void addGrid(final VerticalPanel fp) {
final Grid grid = new Grid(2, 2);
grid.setStyleName(Gerrit.RESOURCES.css().infoBlock());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
index 892aafc..7c51700 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
@@ -40,8 +40,8 @@
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
import com.google.gwtjsonrpc.client.RemoteJsonException;
import java.util.HashSet;
@@ -232,29 +232,32 @@
}
void deleteChecked() {
- final StringBuilder message = new StringBuilder();
- message.append("<b>").append(Gerrit.C.branchDeletionConfirmationMessage()).append("</b>");
- message.append("<p>");
+ final SafeHtmlBuilder b = new SafeHtmlBuilder();
+ b.openElement("b");
+ b.append(Gerrit.C.branchDeletionConfirmationMessage());
+ b.closeElement("b");
+
+ b.openElement("p");
final HashSet<Branch.NameKey> ids = new HashSet<Branch.NameKey>();
for (int row = 1; row < table.getRowCount(); row++) {
final Branch k = getRowItem(row);
if (k != null && table.getWidget(row, 1) instanceof CheckBox
&& ((CheckBox) table.getWidget(row, 1)).getValue()) {
if (!ids.isEmpty()) {
- message.append(", <br>");
+ b.append(",").br();
}
- message.append(k.getName());
+ b.append(k.getName());
ids.add(k.getNameKey());
}
}
- message.append("</p>");
+ b.closeElement("p");
if (ids.isEmpty()) {
return;
}
ConfirmationDialog confirmationDialog =
new ConfirmationDialog(Gerrit.C.branchDeletionDialogTitle(),
- new HTML(message.toString()), new ConfirmationCallback() {
+ b.toSafeHtml(), new ConfirmationCallback() {
@Override
public void onOk() {
Util.PROJECT_SVC.deleteBranch(getProjectKey(), ids,
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java
index 9dac207..d775bbc 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java
@@ -41,7 +41,6 @@
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Panel;
@@ -330,15 +329,15 @@
private void askForConfirmation(final String groupName,
final int memberCount) {
- final StringBuilder message = new StringBuilder();
- message.append("<b>");
- message.append(Util.M.groupManyMembersConfirmation(groupName,
- memberCount));
- message.append("</b>");
+ final SafeHtmlBuilder b = new SafeHtmlBuilder();
+ b.openElement("b");
+ b.append(Util.M
+ .groupManyMembersConfirmation(groupName, memberCount));
+ b.closeElement("b");
final ConfirmationDialog confirmationDialog =
new ConfirmationDialog(Util.C
.approvalTableAddManyReviewersConfirmationDialogTitle(),
- new HTML(message.toString()), new ConfirmationCallback() {
+ b.toSafeHtml(), new ConfirmationCallback() {
@Override
public void onOk() {
addReviewers(reviewers, true);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
index 3cddd2b..aa8e4ab 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
@@ -124,6 +124,7 @@
String abandonChangeTitle();
String oldVersionHistory();
String baseDiffItem();
+ String autoMerge();
String buttonReview();
String buttonPublishCommentsSend();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
index 35991f4..8aadd07 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
@@ -95,6 +95,7 @@
abandonChangeTitle = Code Review - Abandon Change
oldVersionHistory = Old Version History:
baseDiffItem = Base
+autoMerge = Auto Merge
buttonRevertChangeBegin = Revert Change
buttonRevertChangeSend = Revert Change
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
index 1b382a8..dc0df0f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
@@ -250,7 +250,6 @@
}
}
});
- patchesList.addItem(Util.C.baseDiffItem());
patchesGrid = new Grid(1, 2);
patchesGrid.setStyleName(Gerrit.RESOURCES.css().selectPatchSetOldVersion());
@@ -312,6 +311,11 @@
neededBy.display(detail.getNeededBy());
approvals.display(detail);
+ if (detail.getCurrentPatchSetDetail().getInfo().getParents().size() > 1) {
+ patchesList.addItem(Util.C.autoMerge());
+ } else {
+ patchesList.addItem(Util.C.baseDiffItem());
+ }
for (PatchSet pId : detail.getPatchSets()) {
if (patchesList != null) {
patchesList.addItem(Util.M.patchSetHeader(pId.getPatchSetId()), pId
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
index ce55953..d3c3f76 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
@@ -179,10 +179,13 @@
}
}
if (detail.getPatchSet().isDraft()) {
- populatePublishAction();
- }
- if (canDeletePatchSet(detail)) {
- populateDeleteDraftPatchSetAction();
+ if (changeDetail.canPublish()) {
+ populatePublishAction();
+ }
+ if (changeDetail.canDeleteDraft() &&
+ changeDetail.getPatchSets().size() > 1) {
+ populateDeleteDraftPatchSetAction();
+ }
}
}
populateDiffAllActions(detail);
@@ -498,7 +501,8 @@
actionsPanel.add(b);
}
- if (changeDetail.canDeleteDraft()) {
+ if (changeDetail.getChange().getStatus() == Change.Status.DRAFT
+ && changeDetail.canDeleteDraft()) {
final Button b = new Button(Util.C.buttonDeleteDraftChange());
b.addClickHandler(new ClickHandler() {
@Override
@@ -610,9 +614,13 @@
public void onClick(final ClickEvent event) {
b.setEnabled(false);
PatchUtil.DETAIL_SVC.deleteDraftPatchSet(patchSet.getId(),
- new GerritCallback<VoidResult>() {
- public void onSuccess(VoidResult result) {
- Gerrit.display(PageLinks.MINE);
+ new GerritCallback<ChangeDetail>() {
+ public void onSuccess(final ChangeDetail result) {
+ if (result != null) {
+ changeScreen.update(result);
+ } else {
+ Gerrit.display(PageLinks.MINE);
+ }
}
@Override
@@ -708,17 +716,6 @@
changeScreen.update(result);
}
- private boolean canDeletePatchSet(PatchSetDetail detail) {
- if (!detail.getPatchSet().isDraft()) {
- return false;
- }
- // If the draft PS is the only one in a draft change, just delete the change.
- if (changeDetail.getPatchSets().size() <= 1) {
- return false;
- }
- return true;
- }
-
public PatchSet getPatchSet() {
return patchSet;
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetsBlock.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetsBlock.java
index e854cab..f5eea52 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetsBlock.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetsBlock.java
@@ -79,12 +79,6 @@
currentPatchSetId = currps.getId();
patchSets = detail.getPatchSets();
- final List<PatchSet.Id> changePatchSets = new ArrayList<PatchSet.Id>();
-
- for (final PatchSet ps : patchSets) {
- changePatchSets.add(ps.getId());
- }
-
if (Gerrit.isSignedIn()) {
final AccountGeneralPreferences p =
Gerrit.getUserAccount().getGeneralPreferences();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/HistoryTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/HistoryTable.java
index e35097e..3e90050 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/HistoryTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/HistoryTable.java
@@ -76,7 +76,11 @@
table.setText(3, 0, Util.C.patchTableColumnComments());
fmt.setStyleName(3, 0, Gerrit.RESOURCES.css().dataHeader());
- table.setText(0, 1, PatchUtil.C.patchBase());
+ if (screen.getPatchSetDetail().getInfo().getParents().size() > 1) {
+ table.setText(0, 1, PatchUtil.C.patchBaseAutoMerge());
+ } else {
+ table.setText(0, 1, PatchUtil.C.patchBase());
+ }
fmt.setStyleName(0, 1, Gerrit.RESOURCES.css().dataCell());
fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().topMostCell());
fmt.setStyleName(1, 1, Gerrit.RESOURCES.css().dataCell());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java
index 32f7096..fd34729 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java
@@ -27,6 +27,7 @@
String noDifference();
String patchBase();
+ String patchBaseAutoMerge();
String patchHeaderPatchSet();
String patchHeaderOld();
String patchHeaderNew();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties
index f9f407e..6a1dbc1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties
@@ -10,6 +10,7 @@
noDifference = No Differences
patchBase = Base
+patchBaseAutoMerge = Auto Merge
patchHeaderPatchSet = Patch Set
patchHeaderOld = Old Version
patchHeaderNew = New Version
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java
index 1671d6e..83f24b3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java
@@ -189,6 +189,13 @@
}
private void update(AccountDiffPreference dp) {
+ // Did the user just turn on auto-review?
+ if (!reviewed.getValue() && prefs.getOld().isManualReview()
+ && !dp.isManualReview()) {
+ reviewed.setValue(true);
+ setReviewedByCurrentUser(true);
+ }
+
if (lastScript != null && canReuse(dp, lastScript)) {
lastScript.setDiffPrefs(dp);
RpcStatus.INSTANCE.onRpcStart(null);
@@ -450,10 +457,20 @@
bottomNav.display(patchIndex, getPatchScreenType(), fileList);
}
- // Mark this file reviewed as soon we display the diff screen
- if (Gerrit.isSignedIn() && isFirst) {
- reviewed.setValue(true);
- setReviewedByCurrentUser(true /* reviewed */);
+ if (Gerrit.isSignedIn()) {
+ boolean isReviewed = false;
+ if (isFirst && !prefs.get().isManualReview()) {
+ isReviewed = true;
+ setReviewedByCurrentUser(isReviewed);
+ } else {
+ for (Patch p : patchSetDetail.getPatches()) {
+ if (p.getKey().equals(patchKey)) {
+ isReviewed = p.isReviewedByCurrentUser();
+ break;
+ }
+ }
+ }
+ reviewed.setValue(isReviewed);
}
intralineFailure = isFirst && script.hasIntralineFailure();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.java
index df0fff5..941994a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.java
@@ -79,6 +79,9 @@
CheckBox showTabs;
@UiField
+ CheckBox manualReview;
+
+ @UiField
CheckBox skipDeleted;
@UiField
@@ -212,6 +215,7 @@
skipUncommented.setValue(dp.isSkipUncommented());
expandAllComments.setValue(dp.isExpandAllComments());
retainHeader.setValue(dp.isRetainHeader());
+ manualReview.setValue(dp.isManualReview());
}
@UiHandler("update")
@@ -243,6 +247,7 @@
dp.setSkipUncommented(skipUncommented.getValue());
dp.setExpandAllComments(expandAllComments.getValue());
dp.setRetainHeader(retainHeader.getValue());
+ dp.setManualReview(manualReview.getValue());
listenablePrefs.set(dp);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.ui.xml
index e819ffa..2c7afff 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.ui.xml
@@ -147,20 +147,29 @@
</g:CheckBox>
</td>
+ <td valign='bottom' rowspan='2'>
+ <g:CheckBox
+ ui:field='manualReview'
+ text='Manual Review'
+ tabIndex='13'>
+ <ui:attribute name='text'/>
+ </g:CheckBox>
+ </td>
+
<td rowspan='2'>
<br/>
<g:Button
ui:field='update'
text='Update'
styleName='{style.updateButton}'
- tabIndex='13'>
+ tabIndex='14'>
<ui:attribute name='text'/>
</g:Button>
<g:Button
ui:field='save'
text='Save'
styleName='{style.updateButton}'
- tabIndex='14'>
+ tabIndex='15'>
<ui:attribute name='text'/>
</g:Button>
</td>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ListenableAccountDiffPreference.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ListenableAccountDiffPreference.java
index 41de2cd..da4c0d8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ListenableAccountDiffPreference.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ListenableAccountDiffPreference.java
@@ -21,7 +21,7 @@
import com.google.gwtjsonrpc.client.VoidResult;
public class ListenableAccountDiffPreference
- extends ListenableValue<AccountDiffPreference> {
+ extends ListenableOldValue<AccountDiffPreference> {
public ListenableAccountDiffPreference() {
reset();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ListenableOldValue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ListenableOldValue.java
new file mode 100644
index 0000000..5c3e127
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ListenableOldValue.java
@@ -0,0 +1,30 @@
+// 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.client.ui;
+
+public class ListenableOldValue<T> extends ListenableValue<T> {
+
+ private T oldValue;
+
+ public T getOld() {
+ return oldValue;
+ }
+
+ public void set(final T value) {
+ oldValue = get();
+ super.set(value);
+ oldValue = null; // allow it to be gced before the next event
+ }
+}
diff --git a/gerrit-httpd/pom.xml b/gerrit-httpd/pom.xml
index ccc9441..6c2190d 100644
--- a/gerrit-httpd/pom.xml
+++ b/gerrit-httpd/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-httpd</artifactId>
@@ -65,12 +65,6 @@
</dependency>
<dependency>
- <groupId>org.openid4java</groupId>
- <artifactId>openid4java-consumer</artifactId>
- <type>pom</type>
- </dependency>
-
- <dependency>
<groupId>gwtjsonrpc</groupId>
<artifactId>gwtjsonrpc</artifactId>
</dependency>
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CacheBasedWebSession.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CacheBasedWebSession.java
index d9203f5..270b476 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CacheBasedWebSession.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CacheBasedWebSession.java
@@ -15,6 +15,7 @@
package com.google.gerrit.httpd;
import static java.util.concurrent.TimeUnit.HOURS;
+import static java.util.concurrent.TimeUnit.MINUTES;
import com.google.gerrit.httpd.WebSessionManager.Key;
import com.google.gerrit.httpd.WebSessionManager.Val;
@@ -42,6 +43,7 @@
@RequestScoped
public final class CacheBasedWebSession implements WebSession {
private static final String ACCOUNT_COOKIE = "GerritAccount";
+ static final long MAX_AGE_MINUTES = HOURS.toMinutes(12);
public static Module module() {
return new CacheModule() {
@@ -52,7 +54,7 @@
new TypeLiteral<Cache<Key, Val>>() {};
disk(type, cacheName) //
.memoryLimit(1024) // reasonable default for many sites
- .maxAge(12, HOURS) // expire sessions if they are inactive
+ .maxAge(MAX_AGE_MINUTES, MINUTES) // expire sessions if they are inactive
.evictionPolicy(EvictionPolicy.LRU) // keep most recently used
;
bind(WebSessionManager.class);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java
index 97fffd7..c0e3f42 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java
@@ -49,7 +49,7 @@
* <p>
* This filter should only be configured to run, when authentication is
* configured to trust container authentication. This filter is intended only to
- * protect the {@link GitOverHttpFilter} and its handled URLs, which provide remote
+ * protect the {@link GitOverHttpServlet} and its handled URLs, which provide remote
* repository access over HTTP.
*/
@Singleton
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpModule.java
index 63b4737..6fd94c9 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpModule.java
@@ -38,8 +38,8 @@
authFilter = ProjectDigestFilter.class;
}
- String git = GitOverHttpFilter.URL_REGEX;
+ String git = GitOverHttpServlet.URL_REGEX;
filterRegex(git).through(authFilter);
- filterRegex(git).through(GitOverHttpFilter.class);
+ serveRegex(git).with(GitOverHttpServlet.class);
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
similarity index 87%
rename from gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpFilter.java
rename to gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
index 47b5974..145891f 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpFilter.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
@@ -37,7 +37,7 @@
import com.google.inject.name.Named;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.http.server.GitFilter;
+import org.eclipse.jgit.http.server.GitServlet;
import org.eclipse.jgit.http.server.GitSmartHttpTools;
import org.eclipse.jgit.http.server.ServletUtils;
import org.eclipse.jgit.http.server.resolver.AsIsFileService;
@@ -65,20 +65,27 @@
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
/** Serves Git repositories over HTTP. */
@Singleton
-public class GitOverHttpFilter extends GitFilter {
+public class GitOverHttpServlet extends GitServlet {
private static final long serialVersionUID = 1L;
private static final String ATT_CONTROL = ProjectControl.class.getName();
private static final String ATT_RC = ReceiveCommits.class.getName();
private static final String ID_CACHE = "adv_bases";
- public static final String URL_REGEX =
- "^/.*/(?:info/refs|git-upload-pack|git-receive-pack)$";
+ public static final String URL_REGEX;
+ static {
+ StringBuilder url = new StringBuilder();
+ url.append("^(?:/p/|/)(.*/(?:info/refs");
+ for (String name : GitSmartHttpTools.VALID_SERVICES) {
+ url.append('|').append(name);
+ }
+ url.append("))$");
+ URL_REGEX = url.toString();
+ }
static class Module extends AbstractModule {
@Override
@@ -102,7 +109,7 @@
}
@Inject
- GitOverHttpFilter(Resolver resolver,
+ GitOverHttpServlet(Resolver resolver,
UploadFactory upload, UploadFilter uploadFilter,
ReceiveFactory receive, ReceiveFilter receiveFilter) {
setRepositoryResolver(resolver);
@@ -115,29 +122,6 @@
addReceivePackFilter(receiveFilter);
}
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException {
- // JGit doesn't handle parsing the request as-is. It assumes
- // the relevant data is available in getPathInfo(), but this
- // isn't true in GuiceFilter. Massage the request for JGit.
- final HttpServletRequest req = (HttpServletRequest) request;
- HttpServletRequestWrapper wrapped = new HttpServletRequestWrapper(req) {
- @Override
- public String getPathInfo() {
- return req.getRequestURI().substring(1);
- }
-
- @Override
- public String getServletPath() {
- return "/";
- }
- };
- super.doFilter(wrapped, response, chain);
- }
-
-
static class Resolver implements RepositoryResolver<HttpServletRequest> {
private final GitRepositoryManager manager;
private final ProjectControl.Factory projectControlFactory;
@@ -153,9 +137,6 @@
public Repository open(HttpServletRequest req, String projectName)
throws RepositoryNotFoundException, ServiceNotAuthorizedException,
ServiceNotEnabledException {
- if (projectName.startsWith("p/")) {
- projectName = projectName.substring(2);
- }
while (projectName.endsWith("/")) {
projectName = projectName.substring(0, projectName.length() - 1);
}
@@ -170,15 +151,6 @@
}
}
- while (projectName.startsWith("/")) {
- // Be nice and drop the leading "/" if supplied by an absolute path.
- // We don't have a file system hierarchy, just a flat namespace in
- // the database's Project entities. We never encode these with a
- // leading '/' but users might accidentally include them in Git URLs.
- //
- projectName = projectName.substring(1);
- }
-
final ProjectControl pc;
try {
pc = projectControlFactory.controlFor(new Project.NameKey(projectName));
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectDigestFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectDigestFilter.java
index 0c6b01f..c5b0e90 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectDigestFilter.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectDigestFilter.java
@@ -59,7 +59,7 @@
* <p>
* The current HTTP request is authenticated by looking up the username from the
* Authorization header and checking the digest response against the stored
- * password. This filter is intended only to protect the {@link GitOverHttpFilter}
+ * password. This filter is intended only to protect the {@link GitOverHttpServlet}
* and its handled URLs, which provide remote repository access over HTTP.
*
* @see <a href="http://www.ietf.org/rfc/rfc2617.txt">RFC 2617</a>
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
index 3ead232..eb71733 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
@@ -21,7 +21,6 @@
import com.google.gerrit.httpd.auth.container.HttpAuthModule;
import com.google.gerrit.httpd.auth.container.HttpsClientSslCertModule;
import com.google.gerrit.httpd.auth.ldap.LdapAuthModule;
-import com.google.gerrit.httpd.auth.openid.OpenIdModule;
import com.google.gerrit.httpd.gitweb.GitWebModule;
import com.google.gerrit.httpd.rpc.UiRpcModule;
import com.google.gerrit.server.CurrentUser;
@@ -83,10 +82,6 @@
}
switch (authConfig.getAuthType()) {
- case OPENID:
- install(new OpenIdModule());
- break;
-
case HTTP:
case HTTP_LDAP:
install(new HttpAuthModule());
@@ -110,6 +105,8 @@
});
break;
+ case OPENID:
+ // OpenID support is bound in WebAppInitializer and Daemon.
case CUSTOM_EXTENSION:
break;
default:
@@ -119,7 +116,7 @@
install(new UrlModule());
install(new UiRpcModule());
install(new GerritRequestModule());
- install(new GitOverHttpFilter.Module());
+ install(new GitOverHttpServlet.Module());
bind(GitWebConfig.class).toInstance(gitWebConfig);
if (gitWebConfig.getGitwebCGI() != null) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java
index 4a3e113..186d5da 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java
@@ -21,17 +21,22 @@
import static com.google.gerrit.server.ioutil.BasicSerialization.writeFixInt64;
import static com.google.gerrit.server.ioutil.BasicSerialization.writeString;
import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
+import static com.google.gerrit.httpd.CacheBasedWebSession.MAX_AGE_MINUTES;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
+import static java.util.concurrent.TimeUnit.MINUTES;
import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.AccountExternalId;
import com.google.gerrit.server.cache.Cache;
+import com.google.gerrit.server.config.ConfigUtil;
+import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
+import org.eclipse.jgit.lib.Config;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
@@ -47,13 +52,19 @@
return System.currentTimeMillis();
}
+ private final long sessionMaxAgeMillis;
private final SecureRandom prng;
private final Cache<Key, Val> self;
@Inject
- WebSessionManager(@Named(CACHE_NAME) final Cache<Key, Val> cache) {
+ WebSessionManager(@GerritServerConfig Config cfg,
+ @Named(CACHE_NAME) final Cache<Key, Val> cache) {
prng = new SecureRandom();
self = cache;
+
+ sessionMaxAgeMillis = MINUTES.toMillis(ConfigUtil.getTimeUnit(cfg,
+ "cache", CACHE_NAME, "maxAge",
+ MAX_AGE_MINUTES, MINUTES));
}
Key createKey(final Account.Id who) {
@@ -90,7 +101,7 @@
// early but also avoids us needing to refresh the cookie on
// every single request.
//
- final long halfAgeRefresh = self.getTimeToLive(MILLISECONDS) >>> 1;
+ final long halfAgeRefresh = sessionMaxAgeMillis >>> 1;
final long minRefresh = MILLISECONDS.convert(1, HOURS);
final long refresh = Math.min(halfAgeRefresh, minRefresh);
final long refreshCookieAt = now() + refresh;
@@ -114,7 +125,7 @@
// Client may store the cookie until we would remove it from our
// own cache, after which it will certainly be invalid.
//
- return (int) self.getTimeToLive(SECONDS);
+ return (int) MILLISECONDS.toSeconds(sessionMaxAgeMillis);
} else {
// Client should not store the cookie, as the user asked for us
// to not remember them long-term. Sending -1 as the age will
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java
index 777d63e..8cbe271 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java
@@ -29,6 +29,7 @@
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.StarredChange;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountControl;
import com.google.gerrit.server.account.AccountInfoCacheFactory;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
@@ -83,6 +84,7 @@
private final Provider<CurrentUser> currentUser;
private final ChangeControl.Factory changeControlFactory;
private final AccountInfoCacheFactory.Factory accountInfoCacheFactory;
+ private final AccountControl.Factory accountControlFactory;
private final ChangeQueryBuilder.Factory queryBuilder;
private final Provider<ChangeQueryRewriter> queryRewriter;
@@ -92,12 +94,14 @@
final Provider<CurrentUser> currentUser,
final ChangeControl.Factory changeControlFactory,
final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
+ final AccountControl.Factory accountControlFactory,
final ChangeQueryBuilder.Factory queryBuilder,
final Provider<ChangeQueryRewriter> queryRewriter) {
super(schema, currentUser);
this.currentUser = currentUser;
this.changeControlFactory = changeControlFactory;
this.accountInfoCacheFactory = accountInfoCacheFactory;
+ this.accountControlFactory = accountControlFactory;
this.queryBuilder = queryBuilder;
this.queryRewriter = queryRewriter;
}
@@ -217,7 +221,7 @@
Failure {
final AccountInfoCacheFactory ac = accountInfoCacheFactory.create();
final Account user = ac.get(target);
- if (user == null) {
+ if (user == null || !accountControlFactory.get().canSee(user)) {
throw new Failure(new NoSuchEntityException());
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java
index bf77c6a..8edeaec 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java
@@ -28,6 +28,8 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountControl;
+import com.google.gerrit.server.account.AccountVisibility;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.account.GroupMembers;
@@ -61,11 +63,10 @@
private final GroupControl.Factory groupControlFactory;
private final GroupMembers.Factory groupMembersFactory;
private final IdentifiedUser.GenericFactory userFactory;
- private final Provider<CurrentUser> currentUser;
- private final SuggestAccountsEnum suggestAccounts;
+ private final AccountControl.Factory accountControlFactory;
private final Config cfg;
private final GroupCache groupCache;
-
+ private final boolean suggestAccounts;
@Inject
SuggestServiceImpl(final Provider<ReviewDb> schema,
@@ -75,6 +76,7 @@
final GroupMembers.Factory groupMembersFactory,
final IdentifiedUser.GenericFactory userFactory,
final Provider<CurrentUser> currentUser,
+ final AccountControl.Factory accountControlFactory,
@GerritServerConfig final Config cfg, final GroupCache groupCache) {
super(schema, currentUser);
this.projectControlFactory = projectControlFactory;
@@ -83,11 +85,23 @@
this.groupControlFactory = groupControlFactory;
this.groupMembersFactory = groupMembersFactory;
this.userFactory = userFactory;
- this.currentUser = currentUser;
- this.suggestAccounts =
- cfg.getEnum("suggest", null, "accounts", SuggestAccountsEnum.ALL);
+ this.accountControlFactory = accountControlFactory;
this.cfg = cfg;
this.groupCache = groupCache;
+
+ if ("OFF".equals(cfg.getString("suggest", null, "accounts"))) {
+ this.suggestAccounts = false;
+ } else {
+ boolean suggestAccounts;
+ try {
+ AccountVisibility av =
+ cfg.getEnum("suggest", null, "accounts", AccountVisibility.ALL);
+ suggestAccounts = (av != AccountVisibility.NONE);
+ } catch (IllegalArgumentException err) {
+ suggestAccounts = cfg.getBoolean("suggest", null, "accounts", true);
+ }
+ this.suggestAccounts = suggestAccounts;
+ }
}
public void suggestProjectNameKey(final String query, final int limit,
@@ -124,7 +138,7 @@
private List<AccountInfo> suggestAccount(final ReviewDb db,
final String query, final Boolean active, final int limit)
throws OrmException {
- if (suggestAccounts == SuggestAccountsEnum.OFF) {
+ if (!suggestAccounts) {
return Collections.<AccountInfo> emptyList();
}
@@ -166,42 +180,8 @@
if (active != null && active != account.isActive()) {
return;
}
- switch (suggestAccounts) {
- case ALL:
- map.put(account.getId(), info);
- break;
- case SAME_GROUP: {
- Set<AccountGroup.UUID> usersGroups = groupsOf(account);
- usersGroups.remove(AccountGroup.ANONYMOUS_USERS);
- usersGroups.remove(AccountGroup.REGISTERED_USERS);
- for (AccountGroup.UUID myGroup : currentUser.get().getEffectiveGroups()) {
- if (usersGroups.contains(myGroup)) {
- map.put(account.getId(), info);
- break;
- }
- }
- break;
- }
- case VISIBLE_GROUP: {
- Set<AccountGroup.UUID> usersGroups = groupsOf(account);
- usersGroups.remove(AccountGroup.ANONYMOUS_USERS);
- usersGroups.remove(AccountGroup.REGISTERED_USERS);
- for (AccountGroup.UUID usersGroup : usersGroups) {
- try {
- if (groupControlFactory.controlFor(usersGroup).isVisible()) {
- map.put(account.getId(), info);
- break;
- }
- } catch (NoSuchGroupException e) {
- continue;
- }
- }
- break;
- }
- case OFF:
- break;
- default:
- throw new IllegalStateException("Bad SuggestAccounts " + suggestAccounts);
+ if (accountControlFactory.get().canSee(account)) {
+ map.put(account.getId(), info);
}
}
@@ -300,4 +280,4 @@
return true;
}
-}
\ No newline at end of file
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountModule.java
index d2e6118..957f339 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountModule.java
@@ -17,6 +17,7 @@
import com.google.gerrit.httpd.rpc.RpcServletModule;
import com.google.gerrit.httpd.rpc.UiRpcModule;
import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.server.mail.RegisterNewEmailSender;
public class AccountModule extends RpcServletModule {
public AccountModule() {
@@ -34,6 +35,7 @@
factory(ExternalIdDetailFactory.Factory.class);
factory(GroupDetailHandler.Factory.class);
factory(MyGroupsFactory.Factory.class);
+ factory(RegisterNewEmailSender.Factory.class);
factory(RenameGroup.Factory.class);
factory(VisibleGroupsHandler.Factory.class);
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
index fba7107..be28840 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
@@ -14,7 +14,7 @@
package com.google.gerrit.httpd.rpc.account;
-import com.google.gerrit.common.ChangeHookRunner;
+import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.common.data.AccountSecurity;
import com.google.gerrit.common.data.GroupDetail;
import com.google.gerrit.common.errors.ContactInformationStoreException;
@@ -46,21 +46,18 @@
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.contact.ContactStore;
import com.google.gerrit.server.mail.EmailException;
+import com.google.gerrit.server.mail.EmailTokenVerifier;
import com.google.gerrit.server.mail.RegisterNewEmailSender;
import com.google.gerrit.server.ssh.SshKeyCache;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.VoidResult;
-import com.google.gwtjsonrpc.server.ValidToken;
-import com.google.gwtjsonrpc.server.XsrfException;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
-import org.eclipse.jgit.util.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -72,6 +69,7 @@
private final AuthConfig authConfig;
private final Realm realm;
private final Provider<IdentifiedUser> user;
+ private final EmailTokenVerifier emailTokenVerifier;
private final RegisterNewEmailSender.Factory registerNewEmailFactory;
private final SshKeyCache sshKeyCache;
private final AccountByEmailCache byEmailCache;
@@ -86,12 +84,13 @@
private final ExternalIdDetailFactory.Factory externalIdDetailFactory;
private final MyGroupsFactory.Factory myGroupsFactory;
- private final ChangeHookRunner hooks;
+ private final ChangeHooks hooks;
@Inject
AccountSecurityImpl(final Provider<ReviewDb> schema,
final Provider<CurrentUser> currentUser, final ContactStore cs,
final AuthConfig ac, final Realm r, final Provider<IdentifiedUser> u,
+ final EmailTokenVerifier etv,
final RegisterNewEmailSender.Factory esf, final SshKeyCache skc,
final AccountByEmailCache abec, final AccountCache uac,
final AccountManager am,
@@ -101,12 +100,13 @@
final DeleteExternalIds.Factory deleteExternalIdsFactory,
final ExternalIdDetailFactory.Factory externalIdDetailFactory,
final MyGroupsFactory.Factory myGroupsFactory,
- final ChangeHookRunner hooks) {
+ final ChangeHooks hooks) {
super(schema, currentUser);
contactStore = cs;
authConfig = ac;
realm = r;
user = u;
+ emailTokenVerifier = etv;
registerNewEmailFactory = esf;
sshKeyCache = skc;
byEmailCache = abec;
@@ -308,26 +308,18 @@
}
}
- public void validateEmail(final String token,
+ public void validateEmail(final String tokenString,
final AsyncCallback<VoidResult> callback) {
try {
- final ValidToken t =
- authConfig.getEmailRegistrationToken().checkToken(token, null);
- if (t == null || t.getData() == null || "".equals(t.getData())) {
- callback.onFailure(new IllegalStateException("Invalid token"));
- return;
+ EmailTokenVerifier.ParsedToken token = emailTokenVerifier.decode(tokenString);
+ Account.Id currentUser = user.get().getAccountId();
+ if (currentUser.equals(token.getAccountId())) {
+ accountManager.link(currentUser, token.toAuthRequest());
+ callback.onSuccess(VoidResult.INSTANCE);
+ } else {
+ throw new EmailTokenVerifier.InvalidTokenException();
}
- final String newEmail = new String(Base64.decode(t.getData()), "UTF-8");
- if (!newEmail.contains("@")) {
- callback.onFailure(new IllegalStateException("Invalid token"));
- return;
- }
- accountManager.link(user.get().getAccountId(), AuthRequest
- .forEmail(newEmail));
- callback.onSuccess(VoidResult.INSTANCE);
- } catch (XsrfException e) {
- callback.onFailure(e);
- } catch (UnsupportedEncodingException e) {
+ } catch (EmailTokenVerifier.InvalidTokenException e) {
callback.onFailure(e);
} catch (AccountException e) {
callback.onFailure(e);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AbandonChangeHandler.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AbandonChangeHandler.java
index b78af5e..c8bb0f7 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AbandonChangeHandler.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AbandonChangeHandler.java
@@ -14,14 +14,11 @@
package com.google.gerrit.httpd.rpc.changedetail;
-import com.google.gerrit.common.ChangeHookRunner;
import com.google.gerrit.common.data.ChangeDetail;
import com.google.gerrit.common.data.ReviewResult;
import com.google.gerrit.common.errors.NoSuchEntityException;
import com.google.gerrit.httpd.rpc.Handler;
-import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.PatchSet;
-import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.changedetail.AbandonChange;
import com.google.gerrit.server.mail.EmailException;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
index 6529f0f..8b05327 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
@@ -32,7 +32,6 @@
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountInfoCacheFactory;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -122,8 +121,9 @@
detail.setAllowsAnonymous(control.forUser(anonymousUser).isVisible(db));
detail.setCanAbandon(change.getStatus() != Change.Status.DRAFT && change.getStatus().isOpen() && control.canAbandon());
+ detail.setCanPublish(control.canPublish(db));
detail.setCanRestore(change.getStatus() == Change.Status.ABANDONED && control.canRestore());
- detail.setCanDeleteDraft(change.getStatus() == Change.Status.DRAFT && control.isOwner());
+ detail.setCanDeleteDraft(control.canDeleteDraft(db));
detail.setStarred(control.getCurrentUser().getStarredChanges().contains(
changeId));
@@ -160,7 +160,6 @@
private void loadPatchSets() throws OrmException {
ResultSet<PatchSet> source = db.patchSets().byChange(changeId);
List<PatchSet> patches = new ArrayList<PatchSet>();
- CurrentUser user = control.getCurrentUser();
for (PatchSet ps : source) {
if (control.isPatchVisible(ps, db)) {
patches.add(ps);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeManageServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeManageServiceImpl.java
index 88945cb..f055498 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeManageServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeManageServiceImpl.java
@@ -24,7 +24,7 @@
class ChangeManageServiceImpl implements ChangeManageService {
private final SubmitAction.Factory submitAction;
private final AbandonChangeHandler.Factory abandonChangeHandlerFactory;
- private final RestoreChange.Factory restoreChangeFactory;
+ private final RestoreChangeHandler.Factory restoreChangeHandlerFactory;
private final RevertChange.Factory revertChangeFactory;
private final PublishAction.Factory publishAction;
private final DeleteDraftChange.Factory deleteDraftChangeFactory;
@@ -32,13 +32,13 @@
@Inject
ChangeManageServiceImpl(final SubmitAction.Factory patchSetAction,
final AbandonChangeHandler.Factory abandonChangeHandlerFactory,
- final RestoreChange.Factory restoreChangeFactory,
+ final RestoreChangeHandler.Factory restoreChangeHandlerFactory,
final RevertChange.Factory revertChangeFactory,
final PublishAction.Factory publishAction,
final DeleteDraftChange.Factory deleteDraftChangeFactory) {
this.submitAction = patchSetAction;
this.abandonChangeHandlerFactory = abandonChangeHandlerFactory;
- this.restoreChangeFactory = restoreChangeFactory;
+ this.restoreChangeHandlerFactory = restoreChangeHandlerFactory;
this.revertChangeFactory = revertChangeFactory;
this.publishAction = publishAction;
this.deleteDraftChangeFactory = deleteDraftChangeFactory;
@@ -61,7 +61,7 @@
public void restoreChange(final PatchSet.Id patchSetId, final String message,
final AsyncCallback<ChangeDetail> callback) {
- restoreChangeFactory.create(patchSetId, message).to(callback);
+ restoreChangeHandlerFactory.create(patchSetId, message).to(callback);
}
public void publish(final PatchSet.Id patchSetId,
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java
index d91b8e8..4224293 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java
@@ -29,7 +29,7 @@
@Override
protected void configure() {
factory(AbandonChangeHandler.Factory.class);
- factory(RestoreChange.Factory.class);
+ factory(RestoreChangeHandler.Factory.class);
factory(RevertChange.Factory.class);
factory(ChangeDetailFactory.Factory.class);
factory(IncludedInDetailFactory.Factory.class);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/DeleteDraftChange.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/DeleteDraftChange.java
index e389d5f..ab809ce 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/DeleteDraftChange.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/DeleteDraftChange.java
@@ -61,11 +61,11 @@
final Change.Id changeId = patchSetId.getParentKey();
final ChangeControl control = changeControlFactory.validateFor(changeId);
- if (!control.isOwner()) {
+ if (!control.canDeleteDraft(db)) {
throw new NoSuchChangeException(changeId);
}
ChangeUtil.deleteDraftChange(patchSetId, gitManager, replication, db);
return VoidResult.INSTANCE;
}
-}
\ No newline at end of file
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PublishAction.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PublishAction.java
index e41d3ae..f6241fa 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PublishAction.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PublishAction.java
@@ -15,14 +15,12 @@
package com.google.gerrit.httpd.rpc.changedetail;
import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.common.data.ReviewResult;
import com.google.gerrit.common.errors.NoSuchEntityException;
import com.google.gerrit.httpd.rpc.Handler;
-import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.PatchSet;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.changedetail.PublishDraft;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
@@ -33,19 +31,16 @@
PublishAction create(PatchSet.Id patchSetId);
}
- private final ReviewDb db;
+ private final PublishDraft.Factory publishFactory;
private final ChangeDetailFactory.Factory changeDetailFactory;
- private final ChangeControl.Factory changeControlFactory;
private final PatchSet.Id patchSetId;
@Inject
- PublishAction(final ReviewDb db,
+ PublishAction(final PublishDraft.Factory publishFactory,
final ChangeDetailFactory.Factory changeDetailFactory,
- final ChangeControl.Factory changeControlFactory,
@Assisted final PatchSet.Id patchSetId) {
- this.db = db;
- this.changeControlFactory = changeControlFactory;
+ this.publishFactory = publishFactory;
this.changeDetailFactory = changeDetailFactory;
this.patchSetId = patchSetId;
@@ -55,16 +50,10 @@
public ChangeDetail call() throws OrmException, NoSuchEntityException,
IllegalStateException, PatchSetInfoNotAvailableException,
NoSuchChangeException {
-
- final Change.Id changeId = patchSetId.getParentKey();
- final ChangeControl changeControl =
- changeControlFactory.validateFor(changeId);
-
- if (!changeControl.isOwner() && !changeControl.isVisible(db)) {
+ final ReviewResult result = publishFactory.create(patchSetId).call();
+ if (result.getErrors().size() > 0) {
throw new IllegalStateException("Cannot publish patchset");
}
-
- ChangeUtil.publishDraftPatchSet(db, patchSetId);
- return changeDetailFactory.create(changeId).call();
+ return changeDetailFactory.create(result.getChangeId()).call();
}
-}
\ No newline at end of file
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RestoreChange.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RestoreChange.java
deleted file mode 100644
index 22fa568..0000000
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RestoreChange.java
+++ /dev/null
@@ -1,89 +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.httpd.rpc.changedetail;
-
-import com.google.gerrit.common.ChangeHookRunner;
-import com.google.gerrit.common.data.ChangeDetail;
-import com.google.gerrit.common.errors.NoSuchEntityException;
-import com.google.gerrit.httpd.rpc.Handler;
-import com.google.gerrit.reviewdb.Change;
-import com.google.gerrit.reviewdb.PatchSet;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.mail.EmailException;
-import com.google.gerrit.server.mail.RestoredSender;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.InvalidChangeOperationException;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import javax.annotation.Nullable;
-
-class RestoreChange extends Handler<ChangeDetail> {
- interface Factory {
- RestoreChange create(PatchSet.Id patchSetId, String message);
- }
-
- private final ChangeControl.Factory changeControlFactory;
- private final ReviewDb db;
- private final IdentifiedUser currentUser;
- private final RestoredSender.Factory senderFactory;
- private final ChangeDetailFactory.Factory changeDetailFactory;
-
- private final PatchSet.Id patchSetId;
- @Nullable
- private final String message;
-
- private final ChangeHookRunner hooks;
-
- @Inject
- RestoreChange(final ChangeControl.Factory changeControlFactory,
- final ReviewDb db, final IdentifiedUser currentUser,
- final RestoredSender.Factory senderFactory,
- final ChangeDetailFactory.Factory changeDetailFactory,
- @Assisted final PatchSet.Id patchSetId,
- @Assisted @Nullable final String message, final ChangeHookRunner hooks) {
- this.changeControlFactory = changeControlFactory;
- this.db = db;
- this.currentUser = currentUser;
- this.senderFactory = senderFactory;
- this.changeDetailFactory = changeDetailFactory;
-
- this.patchSetId = patchSetId;
- this.message = message;
- this.hooks = hooks;
- }
-
- @Override
- public ChangeDetail call() throws NoSuchChangeException, OrmException,
- EmailException, NoSuchEntityException, InvalidChangeOperationException,
- PatchSetInfoNotAvailableException {
-
- final Change.Id changeId = patchSetId.getParentKey();
- final ChangeControl control = changeControlFactory.validateFor(changeId);
- if (!control.canRestore()) {
- throw new NoSuchChangeException(changeId);
- }
-
- ChangeUtil.restore(patchSetId, currentUser, message, db, senderFactory,
- hooks);
-
- return changeDetailFactory.create(changeId).call();
- }
-}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RestoreChangeHandler.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RestoreChangeHandler.java
new file mode 100644
index 0000000..667861a
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RestoreChangeHandler.java
@@ -0,0 +1,68 @@
+// 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.httpd.rpc.changedetail;
+
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.common.data.ReviewResult;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.server.changedetail.RestoreChange;
+import com.google.gerrit.server.mail.EmailException;
+import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.project.InvalidChangeOperationException;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import javax.annotation.Nullable;
+
+class RestoreChangeHandler extends Handler<ChangeDetail> {
+ interface Factory {
+ RestoreChangeHandler create(PatchSet.Id patchSetId, String message);
+ }
+
+ private final RestoreChange.Factory restoreChangeFactory;
+ private final ChangeDetailFactory.Factory changeDetailFactory;
+
+ private final PatchSet.Id patchSetId;
+ @Nullable
+ private final String message;
+
+ @Inject
+ RestoreChangeHandler(final RestoreChange.Factory restoreChangeFactory,
+ final ChangeDetailFactory.Factory changeDetailFactory,
+ @Assisted final PatchSet.Id patchSetId,
+ @Assisted @Nullable final String message) {
+ this.restoreChangeFactory = restoreChangeFactory;
+ this.changeDetailFactory = changeDetailFactory;
+
+ this.patchSetId = patchSetId;
+ this.message = message;
+ }
+
+ @Override
+ public ChangeDetail call() throws NoSuchChangeException, OrmException,
+ EmailException, NoSuchEntityException, InvalidChangeOperationException,
+ PatchSetInfoNotAvailableException {
+ final ReviewResult result =
+ restoreChangeFactory.create(patchSetId, message).call();
+ if (result.getErrors().size() > 0) {
+ throw new NoSuchChangeException(result.getChangeId());
+ }
+ return changeDetailFactory.create(result.getChangeId()).call();
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RevertChange.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RevertChange.java
index 5e007fa..da74941 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RevertChange.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RevertChange.java
@@ -14,7 +14,7 @@
package com.google.gerrit.httpd.rpc.changedetail;
-import com.google.gerrit.common.ChangeHookRunner;
+import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.common.data.ChangeDetail;
import com.google.gerrit.common.errors.NoSuchEntityException;
import com.google.gerrit.httpd.rpc.Handler;
@@ -60,7 +60,7 @@
@Nullable
private final String message;
- private final ChangeHookRunner hooks;
+ private final ChangeHooks hooks;
private final GitRepositoryManager gitManager;
private final PatchSetInfoFactory patchSetInfoFactory;
@@ -73,7 +73,7 @@
final RevertedSender.Factory revertedSenderFactory,
final ChangeDetailFactory.Factory changeDetailFactory,
@Assisted final PatchSet.Id patchSetId,
- @Assisted @Nullable final String message, final ChangeHookRunner hooks,
+ @Assisted @Nullable final String message, final ChangeHooks hooks,
final GitRepositoryManager gitManager,
final PatchSetInfoFactory patchSetInfoFactory,
final ReplicationQueue replication,
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/SubmitAction.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/SubmitAction.java
index ec003f1..38574bc 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/SubmitAction.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/SubmitAction.java
@@ -15,109 +15,48 @@
package com.google.gerrit.httpd.rpc.changedetail;
import com.google.gerrit.common.data.ChangeDetail;
-import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.common.data.ReviewResult;
import com.google.gerrit.common.errors.NoSuchEntityException;
import com.google.gerrit.httpd.rpc.Handler;
-import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.PatchSet;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.git.MergeOp;
-import com.google.gerrit.server.git.MergeQueue;
+import com.google.gerrit.server.changedetail.Submit;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
-import java.util.List;
-
class SubmitAction extends Handler<ChangeDetail> {
interface Factory {
SubmitAction create(PatchSet.Id patchSetId);
}
- private final ReviewDb db;
- private final MergeQueue merger;
- private final IdentifiedUser user;
+ private final Submit.Factory submitFactory;
private final ChangeDetailFactory.Factory changeDetailFactory;
- private final ChangeControl.Factory changeControlFactory;
- private final MergeOp.Factory opFactory;
private final PatchSet.Id patchSetId;
@Inject
- SubmitAction(final ReviewDb db, final MergeQueue mq,
- final IdentifiedUser user,
+ SubmitAction(final Submit.Factory submitFactory,
final ChangeDetailFactory.Factory changeDetailFactory,
- final ChangeControl.Factory changeControlFactory,
- final MergeOp.Factory opFactory,
@Assisted final PatchSet.Id patchSetId) {
- this.db = db;
- this.merger = mq;
- this.user = user;
- this.changeControlFactory = changeControlFactory;
+ this.submitFactory = submitFactory;
this.changeDetailFactory = changeDetailFactory;
- this.opFactory = opFactory;
this.patchSetId = patchSetId;
}
@Override
public ChangeDetail call() throws OrmException, NoSuchEntityException,
- IllegalStateException, PatchSetInfoNotAvailableException,
- NoSuchChangeException {
-
- final Change.Id changeId = patchSetId.getParentKey();
- final ChangeControl changeControl =
- changeControlFactory.validateFor(changeId);
-
- List<SubmitRecord> result = changeControl.canSubmit(db, patchSetId);
- if (result.isEmpty()) {
- throw new IllegalStateException("Cannot submit");
+ IllegalStateException, InvalidChangeOperationException,
+ PatchSetInfoNotAvailableException, NoSuchChangeException {
+ final ReviewResult result =
+ submitFactory.create(patchSetId).call();
+ if (result.getErrors().size() > 0) {
+ throw new IllegalStateException(
+ "Cannot submit " + result.getErrors().get(0).getMessageOrType());
}
-
- switch (result.get(0).status) {
- case OK:
- ChangeUtil.submit(patchSetId, user, db, opFactory, merger);
- return changeDetailFactory.create(changeId).call();
-
- case NOT_READY: {
- for (SubmitRecord.Label lbl : result.get(0).labels) {
- switch (lbl.status) {
- case OK:
- break;
-
- case REJECT:
- throw new IllegalStateException("Blocked by " + lbl.label);
-
- case NEED:
- throw new IllegalStateException("Needs " + lbl.label);
-
- case IMPOSSIBLE:
- throw new IllegalStateException("Cannnot submit, check project access");
-
- default:
- throw new IllegalArgumentException("Unknown status " + lbl.status);
- }
- }
- throw new IllegalStateException("Cannot submit");
- }
-
- case CLOSED:
- throw new IllegalStateException("Change is closed");
-
- case RULE_ERROR:
- if (result.get(0).errorMessage != null) {
- throw new IllegalStateException(result.get(0).errorMessage);
- } else {
- throw new IllegalStateException("Internal rule error");
- }
-
- default:
- throw new IllegalStateException("Uknown status " + result.get(0).status);
- }
+ return changeDetailFactory.create(result.getChangeId()).call();
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java
index 6e0e1af..75b3e40 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java
@@ -14,7 +14,9 @@
package com.google.gerrit.httpd.rpc.patch;
+import com.google.gerrit.common.data.ChangeDetail;
import com.google.gerrit.common.data.ReviewerResult;
+import com.google.gerrit.common.data.ReviewResult;
import com.google.gerrit.common.data.ApprovalSummary;
import com.google.gerrit.common.data.ApprovalSummarySet;
import com.google.gerrit.common.data.ApprovalTypes;
@@ -23,6 +25,7 @@
import com.google.gerrit.common.errors.NoSuchEntityException;
import com.google.gerrit.httpd.rpc.BaseServiceImplementation;
import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.httpd.rpc.changedetail.ChangeDetailFactory;
import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.AccountDiffPreference;
import com.google.gerrit.reviewdb.AccountPatchReview;
@@ -35,12 +38,9 @@
import com.google.gerrit.reviewdb.PatchSetApproval;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.Patch.Key;
-import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountInfoCacheFactory;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.ReplicationQueue;
-import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gerrit.server.changedetail.DeleteDraftPatchSet;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.patch.PublishComments;
import com.google.gerrit.server.project.ChangeControl;
@@ -52,7 +52,6 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
-import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -66,14 +65,13 @@
private final AccountInfoCacheFactory.Factory accountInfoCacheFactory;
private final AddReviewerHandler.Factory addReviewerHandlerFactory;
private final ChangeControl.Factory changeControlFactory;
+ private final DeleteDraftPatchSet.Factory deleteDraftPatchSetFactory;
private final RemoveReviewerHandler.Factory removeReviewerHandlerFactory;
private final FunctionState.Factory functionStateFactory;
private final PublishComments.Factory publishCommentsFactory;
private final PatchScriptFactory.Factory patchScriptFactoryFactory;
private final SaveDraft.Factory saveDraftFactory;
- private final PatchSetInfoFactory patchSetInfoFactory;
- private final GitRepositoryManager gitManager;
- private final ReplicationQueue replication;
+ private final ChangeDetailFactory.Factory changeDetailFactory;
@Inject
PatchDetailServiceImpl(final Provider<ReviewDb> schema,
@@ -83,13 +81,12 @@
final AddReviewerHandler.Factory addReviewerHandlerFactory,
final RemoveReviewerHandler.Factory removeReviewerHandlerFactory,
final ChangeControl.Factory changeControlFactory,
+ final DeleteDraftPatchSet.Factory deleteDraftPatchSetFactory,
final FunctionState.Factory functionStateFactory,
final PatchScriptFactory.Factory patchScriptFactoryFactory,
final PublishComments.Factory publishCommentsFactory,
final SaveDraft.Factory saveDraftFactory,
- final PatchSetInfoFactory patchSetInfoFactory,
- final GitRepositoryManager gitManager,
- final ReplicationQueue replication) {
+ final ChangeDetailFactory.Factory changeDetailFactory) {
super(schema, currentUser);
this.approvalTypes = approvalTypes;
@@ -97,13 +94,12 @@
this.addReviewerHandlerFactory = addReviewerHandlerFactory;
this.removeReviewerHandlerFactory = removeReviewerHandlerFactory;
this.changeControlFactory = changeControlFactory;
+ this.deleteDraftPatchSetFactory = deleteDraftPatchSetFactory;
this.functionStateFactory = functionStateFactory;
this.patchScriptFactoryFactory = patchScriptFactoryFactory;
this.publishCommentsFactory = publishCommentsFactory;
this.saveDraftFactory = saveDraftFactory;
- this.patchSetInfoFactory = patchSetInfoFactory;
- this.gitManager = gitManager;
- this.replication = replication;
+ this.changeDetailFactory = changeDetailFactory;
}
public void patchScript(final Patch.Key patchKey, final PatchSet.Id psa,
@@ -149,23 +145,28 @@
}
public void deleteDraftPatchSet(final PatchSet.Id psid,
- final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(ReviewDb db) throws OrmException, Failure {
+ final AsyncCallback<ChangeDetail> callback) {
+ run(callback, new Action<ChangeDetail>() {
+ public ChangeDetail run(ReviewDb db) throws OrmException, Failure {
+ ReviewResult result = null;
try {
- final ChangeControl cc = changeControlFactory.validateFor(psid.getParentKey());
- if (!cc.isOwner()) {
+ result = deleteDraftPatchSetFactory.create(psid).call();
+ if (result.getErrors().size() > 0) {
throw new Failure(new NoSuchEntityException());
}
- ChangeUtil.deleteDraftPatchSet(psid, gitManager, replication, patchSetInfoFactory, db);
+ if (result.getChangeId() == null) {
+ // the change was deleted because the draft patch set that was
+ // deleted was the only patch set in the change
+ return null;
+ }
+ return changeDetailFactory.create(result.getChangeId()).call();
} catch (NoSuchChangeException e) {
- throw new Failure(new NoSuchChangeException(psid.getParentKey()));
+ throw new Failure(new NoSuchChangeException(result.getChangeId()));
+ } catch (NoSuchEntityException e) {
+ throw new Failure(e);
} catch (PatchSetInfoNotAvailableException e) {
throw new Failure(e);
- } catch (IOException e) {
- throw new Failure(e);
}
- return VoidResult.INSTANCE;
}
});
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddBranch.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddBranch.java
index 1aad23f..db03f16 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddBranch.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddBranch.java
@@ -14,7 +14,7 @@
package com.google.gerrit.httpd.rpc.project;
-import com.google.gerrit.common.ChangeHookRunner;
+import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.common.data.ListBranchesResult;
import com.google.gerrit.common.errors.InvalidNameException;
import com.google.gerrit.common.errors.InvalidRevisionException;
@@ -60,7 +60,7 @@
private final IdentifiedUser identifiedUser;
private final GitRepositoryManager repoManager;
private final ReplicationQueue replication;
- private final ChangeHookRunner hooks;
+ private final ChangeHooks hooks;
private final Project.NameKey projectName;
private final String branchName;
@@ -72,7 +72,7 @@
final IdentifiedUser identifiedUser,
final GitRepositoryManager repoManager,
final ReplicationQueue replication,
- final ChangeHookRunner hooks,
+ final ChangeHooks hooks,
@Assisted Project.NameKey projectName,
@Assisted("branchName") String branchName,
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteBranches.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteBranches.java
index d3eae24..0f19bbc 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteBranches.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteBranches.java
@@ -14,7 +14,7 @@
package com.google.gerrit.httpd.rpc.project;
-import com.google.gerrit.common.ChangeHookRunner;
+import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.Branch;
import com.google.gerrit.reviewdb.Project;
@@ -49,7 +49,7 @@
private final GitRepositoryManager repoManager;
private final ReplicationQueue replication;
private final IdentifiedUser identifiedUser;
- private final ChangeHookRunner hooks;
+ private final ChangeHooks hooks;
private final Project.NameKey projectName;
private final Set<Branch.NameKey> toRemove;
@@ -59,7 +59,7 @@
final GitRepositoryManager repoManager,
final ReplicationQueue replication,
final IdentifiedUser identifiedUser,
- final ChangeHookRunner hooks,
+ final ChangeHooks hooks,
@Assisted Project.NameKey name, @Assisted Set<Branch.NameKey> toRemove) {
this.projectControlFactory = projectControlFactory;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
index 7c6584a..bf7eeae 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
@@ -42,6 +42,7 @@
private final ProjectAccessFactory.Factory projectAccessFactory;
private final CreateProjectHandler.Factory createProjectHandlerFactory;
private final ProjectDetailFactory.Factory projectDetailFactory;
+ private final SuggestParentCandidatesHandler.Factory suggestParentCandidatesHandlerFactory;
@Inject
ProjectAdminServiceImpl(final AddBranch.Factory addBranchFactory,
@@ -53,6 +54,7 @@
final VisibleProjectDetails.Factory visibleProjectDetailsFactory,
final ProjectAccessFactory.Factory projectAccessFactory,
final ProjectDetailFactory.Factory projectDetailFactory,
+ final SuggestParentCandidatesHandler.Factory parentCandidatesFactory,
final CreateProjectHandler.Factory createNewProjectFactory) {
this.addBranchFactory = addBranchFactory;
this.changeProjectAccessFactory = changeProjectAccessFactory;
@@ -64,6 +66,7 @@
this.projectAccessFactory = projectAccessFactory;
this.projectDetailFactory = projectDetailFactory;
this.createProjectHandlerFactory = createNewProjectFactory;
+ this.suggestParentCandidatesHandlerFactory = parentCandidatesFactory;
}
@Override
@@ -77,6 +80,11 @@
}
@Override
+ public void suggestParentCandidates(AsyncCallback<List<Project>> callback) {
+ suggestParentCandidatesHandlerFactory.create().to(callback);
+ }
+
+ @Override
public void projectDetail(final Project.NameKey projectName,
final AsyncCallback<ProjectDetail> callback) {
projectDetailFactory.create(projectName).to(callback);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
index 5678045..2eb55b3 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
@@ -38,6 +38,7 @@
factory(VisibleProjectDetails.Factory.class);
factory(ProjectAccessFactory.Factory.class);
factory(ProjectDetailFactory.Factory.class);
+ factory(SuggestParentCandidatesHandler.Factory.class);
}
});
rpc(ProjectAdminServiceImpl.class);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/SuggestParentCandidatesHandler.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/SuggestParentCandidatesHandler.java
new file mode 100644
index 0000000..a6442e3
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/SuggestParentCandidatesHandler.java
@@ -0,0 +1,40 @@
+// 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.httpd.rpc.project;
+
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.server.project.SuggestParentCandidates;
+import com.google.inject.Inject;
+
+import java.util.List;
+
+public class SuggestParentCandidatesHandler extends Handler<List<Project>> {
+ interface Factory {
+ SuggestParentCandidatesHandler create();
+ }
+
+ private final SuggestParentCandidates suggestParentCandidates;
+
+ @Inject
+ SuggestParentCandidatesHandler(final SuggestParentCandidates suggestParentCandidates) {
+ this.suggestParentCandidates = suggestParentCandidates;
+ }
+
+ @Override
+ public List<Project> call() throws Exception {
+ return suggestParentCandidates.getProjects();
+ }
+}
diff --git a/gerrit-launcher/pom.xml b/gerrit-launcher/pom.xml
index a5b0159..5707a22 100644
--- a/gerrit-launcher/pom.xml
+++ b/gerrit-launcher/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-launcher</artifactId>
diff --git a/gerrit-main/pom.xml b/gerrit-main/pom.xml
index f630be64..96f1886 100644
--- a/gerrit-main/pom.xml
+++ b/gerrit-main/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-main</artifactId>
diff --git a/gerrit-openid/.gitignore b/gerrit-openid/.gitignore
new file mode 100644
index 0000000..194bedc
--- /dev/null
+++ b/gerrit-openid/.gitignore
@@ -0,0 +1,5 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
+/.settings/org.eclipse.m2e.core.prefs
\ No newline at end of file
diff --git a/gerrit-openid/.settings/org.eclipse.core.resources.prefs b/gerrit-openid/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..fc11c3f
--- /dev/null
+++ b/gerrit-openid/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,5 @@
+#Thu Jul 28 11:02:36 PDT 2011
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/test/java=UTF-8
+encoding/<project>=UTF-8
diff --git a/gerrit-openid/.settings/org.eclipse.core.runtime.prefs b/gerrit-openid/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000..8667cfd
--- /dev/null
+++ b/gerrit-openid/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-openid/.settings/org.eclipse.jdt.core.prefs b/gerrit-openid/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..470942d
--- /dev/null
+++ b/gerrit-openid/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,269 @@
+#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
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-openid/.settings/org.eclipse.jdt.ui.prefs b/gerrit-openid/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..d4218a5
--- /dev/null
+++ b/gerrit-openid/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-openid/pom.xml b/gerrit-openid/pom.xml
new file mode 100644
index 0000000..74328cc
--- /dev/null
+++ b/gerrit-openid/pom.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<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.3-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-openid</artifactId>
+ <name>Gerrit Code Review - OpenID servlet and RPC</name>
+
+ <description>
+ OpenID
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit.http.server</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-servlet</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openid4java</groupId>
+ <artifactId>openid4java-consumer</artifactId>
+ <type>pom</type>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-httpd</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-server</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdLoginServlet.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdLoginServlet.java
similarity index 100%
rename from gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdLoginServlet.java
rename to gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdLoginServlet.java
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdModule.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdModule.java
similarity index 100%
rename from gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdModule.java
rename to gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdModule.java
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
similarity index 100%
rename from gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
rename to gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/XrdsFilter.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/XrdsFilter.java
similarity index 100%
rename from gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/XrdsFilter.java
rename to gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/XrdsFilter.java
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/XrdsServlet.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/XrdsServlet.java
similarity index 100%
rename from gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/XrdsServlet.java
rename to gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/XrdsServlet.java
diff --git a/gerrit-patch-commonsnet/pom.xml b/gerrit-patch-commonsnet/pom.xml
index 738d6ce..f267920 100644
--- a/gerrit-patch-commonsnet/pom.xml
+++ b/gerrit-patch-commonsnet/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-patch-commonsnet</artifactId>
diff --git a/gerrit-patch-jgit/pom.xml b/gerrit-patch-jgit/pom.xml
index f2d1cfd..4c2aa0b 100644
--- a/gerrit-patch-jgit/pom.xml
+++ b/gerrit-patch-jgit/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-patch-jgit</artifactId>
diff --git a/gerrit-pgm/pom.xml b/gerrit-pgm/pom.xml
index 54f16a7..e816797 100644
--- a/gerrit-pgm/pom.xml
+++ b/gerrit-pgm/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-pgm</artifactId>
@@ -69,6 +69,12 @@
<dependency>
<groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-openid</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
<artifactId>gerrit-sshd</artifactId>
<version>${project.version}</version>
</dependency>
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
index 73ddd50..7382096 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -16,11 +16,14 @@
import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
+import com.google.gerrit.common.ChangeHookRunner;
+import com.google.gerrit.ehcache.EhcachePoolImpl;
import com.google.gerrit.httpd.CacheBasedWebSession;
import com.google.gerrit.httpd.GitOverHttpModule;
import com.google.gerrit.httpd.HttpCanonicalWebUrlProvider;
import com.google.gerrit.httpd.WebModule;
import com.google.gerrit.httpd.WebSshGlueModule;
+import com.google.gerrit.httpd.auth.openid.OpenIdModule;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.pgm.http.jetty.JettyEnv;
import com.google.gerrit.pgm.http.jetty.JettyModule;
@@ -29,6 +32,8 @@
import com.google.gerrit.pgm.util.LogFileCompressor;
import com.google.gerrit.pgm.util.RuntimeShutdown;
import com.google.gerrit.pgm.util.SiteProgram;
+import com.google.gerrit.reviewdb.AuthType;
+import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.AuthConfigModule;
import com.google.gerrit.server.config.CanonicalWebUrlModule;
import com.google.gerrit.server.config.CanonicalWebUrlProvider;
@@ -37,6 +42,7 @@
import com.google.gerrit.server.contact.HttpContactStoreConnection;
import com.google.gerrit.server.git.PushReplication;
import com.google.gerrit.server.git.WorkQueue;
+import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
import com.google.gerrit.server.mail.SmtpEmailSender;
import com.google.gerrit.server.schema.SchemaVersionCheck;
import com.google.gerrit.server.ssh.NoSshModule;
@@ -193,8 +199,11 @@
modules.add(SchemaVersionCheck.module());
modules.add(new LogFileCompressor.Module());
modules.add(new WorkQueue.Module());
+ modules.add(new ChangeHookRunner.Module());
modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
+ modules.add(new EhcachePoolImpl.Module());
modules.add(new SmtpEmailSender.Module());
+ modules.add(new SignedTokenEmailTokenVerifier.Module());
modules.add(new PushReplication.Module());
if (httpd) {
modules.add(new CanonicalWebUrlModule() {
@@ -252,14 +261,20 @@
final List<Module> modules = new ArrayList<Module>();
modules.add(CacheBasedWebSession.module());
modules.add(HttpContactStoreConnection.module());
- modules.add(sysInjector.getInstance(WebModule.class));
modules.add(sysInjector.getInstance(GitOverHttpModule.class));
+ modules.add(sysInjector.getInstance(WebModule.class));
if (sshd) {
modules.add(sshInjector.getInstance(WebSshGlueModule.class));
modules.add(new ProjectQoSFilter.Module());
} else {
modules.add(new NoSshModule());
}
+
+ AuthConfig authConfig = cfgInjector.getInstance(AuthConfig.class);
+ if (authConfig.getAuthType() == AuthType.OPENID) {
+ modules.add(new OpenIdModule());
+ }
+
return sysInjector.createChildInjector(modules);
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java
index 7b8c286..7842b6c 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java
@@ -17,6 +17,7 @@
import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.ehcache.EhcachePoolImpl;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.pgm.util.SiteProgram;
@@ -100,6 +101,7 @@
install(AccountCacheImpl.module());
install(GroupCacheImpl.module());
+ install(new EhcachePoolImpl.Module());
install(new FactoryModule() {
@Override
protected void configure() {
@@ -109,7 +111,6 @@
install(new LifecycleModule() {
@Override
protected void configure() {
- listener().to(CachePool.Lifecycle.class);
listener().to(LocalDiskRepositoryManager.Lifecycle.class);
}
});
diff --git a/gerrit-prettify/pom.xml b/gerrit-prettify/pom.xml
index 3ac4deb..4f05573 100644
--- a/gerrit-prettify/pom.xml
+++ b/gerrit-prettify/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-prettify</artifactId>
diff --git a/gerrit-reviewdb/pom.xml b/gerrit-reviewdb/pom.xml
index 8c43204..5e8a37c 100644
--- a/gerrit-reviewdb/pom.xml
+++ b/gerrit-reviewdb/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-reviewdb</artifactId>
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountDiffPreference.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountDiffPreference.java
index c362f26..ccc379e 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountDiffPreference.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountDiffPreference.java
@@ -65,6 +65,7 @@
p.setIntralineDifference(true);
p.setShowTabs(true);
p.setContext(DEFAULT_CONTEXT);
+ p.setManualReview(false);
return p;
}
@@ -108,6 +109,9 @@
@Column(id = 13)
protected boolean retainHeader;
+ @Column(id = 14)
+ protected boolean manualReview;
+
protected AccountDiffPreference() {
}
@@ -129,6 +133,7 @@
this.expandAllComments = p.expandAllComments;
this.context = p.context;
this.retainHeader = p.retainHeader;
+ this.manualReview = p.manualReview;
}
public Account.Id getAccountId() {
@@ -233,4 +238,12 @@
public void setRetainHeader(boolean retain) {
retainHeader = retain;
}
+
+ public boolean isManualReview() {
+ return manualReview;
+ }
+
+ public void setManualReview(boolean manual) {
+ manualReview = manual;
+ }
}
diff --git a/gerrit-server/pom.xml b/gerrit-server/pom.xml
index 1c766f6..20a7286 100644
--- a/gerrit-server/pom.xml
+++ b/gerrit-server/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-server</artifactId>
@@ -54,11 +54,6 @@
</dependency>
<dependency>
- <groupId>net.sf.ehcache</groupId>
- <artifactId>ehcache-core</artifactId>
- </dependency>
-
- <dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
</dependency>
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
index 9de9483..dc8b807 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
@@ -46,6 +46,7 @@
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gwtorm.client.OrmException;
+import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -67,14 +68,20 @@
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
-/**
- * This class implements hooks for certain gerrit events.
- */
+/** Spawns local executables when a hook action occurs. */
@Singleton
-public class ChangeHookRunner {
+public class ChangeHookRunner implements ChangeHooks {
/** A logger for this class. */
private static final Logger log = LoggerFactory.getLogger(ChangeHookRunner.class);
+ public static class Module extends AbstractModule {
+ @Override
+ protected void configure() {
+ bind(ChangeHookRunner.class);
+ bind(ChangeHooks.class).to(ChangeHookRunner.class);
+ }
+ }
+
private static class ChangeListenerHolder {
final ChangeListener listener;
final IdentifiedUser user;
@@ -216,13 +223,6 @@
}
}
- /**
- * Fire the Patchset Created Hook.
- *
- * @param change The change itself.
- * @param patchSet The Patchset that was created.
- * @throws OrmException
- */
public void doPatchsetCreatedHook(final Change change, final PatchSet patchSet,
final ReviewDb db) throws OrmException {
final PatchSetCreatedEvent event = new PatchSetCreatedEvent();
@@ -242,19 +242,9 @@
addArg(args, "--commit", event.patchSet.revision);
addArg(args, "--patchset", event.patchSet.number);
- runHook(openRepository(change), patchsetCreatedHook, args);
+ runHook(change.getProject(), patchsetCreatedHook, args);
}
- /**
- * Fire the Comment Added Hook.
- *
- * @param change The change itself.
- * @param patchSet The patchset this comment is related to.
- * @param account The gerrit user who commited the change.
- * @param comment The comment given.
- * @param approvals Map of Approval Categories and Scores
- * @throws OrmException
- */
public void doCommentAddedHook(final Change change, final Account account,
final PatchSet patchSet, final String comment, final Map<ApprovalCategory.Id,
ApprovalCategoryValue.Id> approvals, final ReviewDb db) throws OrmException {
@@ -287,17 +277,9 @@
addArg(args, "--" + approval.getKey().get(), Short.toString(approval.getValue().get()));
}
- runHook(openRepository(change), commentAddedHook, args);
+ runHook(change.getProject(), commentAddedHook, args);
}
- /**
- * Fire the Change Merged Hook.
- *
- * @param change The change itself.
- * @param account The gerrit user who commited the change.
- * @param patchSet The patchset that was merged.
- * @throws OrmException
- */
public void doChangeMergedHook(final Change change, final Account account,
final PatchSet patchSet, final ReviewDb db) throws OrmException {
final ChangeMergedEvent event = new ChangeMergedEvent();
@@ -315,17 +297,9 @@
addArg(args, "--submitter", getDisplayName(account));
addArg(args, "--commit", event.patchSet.revision);
- runHook(openRepository(change), changeMergedHook, args);
+ runHook(change.getProject(), changeMergedHook, args);
}
- /**
- * Fire the Change Abandoned Hook.
- *
- * @param change The change itself.
- * @param account The gerrit user who abandoned the change.
- * @param reason Reason for abandoning the change.
- * @throws OrmException
- */
public void doChangeAbandonedHook(final Change change, final Account account,
final String reason, final ReviewDb db) throws OrmException {
final ChangeAbandonedEvent event = new ChangeAbandonedEvent();
@@ -343,17 +317,9 @@
addArg(args, "--abandoner", getDisplayName(account));
addArg(args, "--reason", reason == null ? "" : reason);
- runHook(openRepository(change), changeAbandonedHook, args);
+ runHook(change.getProject(), changeAbandonedHook, args);
}
- /**
- * Fire the Change Restored Hook.
- *
- * @param change The change itself.
- * @param account The gerrit user who restored the change.
- * @param reason Reason for restoring the change.
- * @throws OrmException
- */
public void doChangeRestoreHook(final Change change, final Account account,
final String reason, final ReviewDb db) throws OrmException {
final ChangeRestoreEvent event = new ChangeRestoreEvent();
@@ -371,26 +337,13 @@
addArg(args, "--restorer", getDisplayName(account));
addArg(args, "--reason", reason == null ? "" : reason);
- runHook(openRepository(change), changeRestoredHook, args);
+ runHook(change.getProject(), changeRestoredHook, args);
}
- /**
- * Fire the Ref Updated Hook
- * @param project The project the ref update occured on
- * @param refUpdate An actual RefUpdate object
- * @param account The gerrit user who moved the ref
- */
public void doRefUpdatedHook(final Branch.NameKey refName, final RefUpdate refUpdate, final Account account) {
doRefUpdatedHook(refName, refUpdate.getOldObjectId(), refUpdate.getNewObjectId(), account);
}
- /**
- * Fire the Ref Updated Hook
- * @param refName The Branch.NameKey of the ref that was updated
- * @param oldId The ref's old id
- * @param newId The ref's new id
- * @param account The gerrit user who moved the ref
- */
public void doRefUpdatedHook(final Branch.NameKey refName, final ObjectId oldId, final ObjectId newId, final Account account) {
final RefUpdatedEvent event = new RefUpdatedEvent();
@@ -409,7 +362,7 @@
addArg(args, "--submitter", getDisplayName(account));
}
- runHook(openRepository(refName.getParentKey()), refUpdatedHook, args);
+ runHook(refName.getParentKey(), refUpdatedHook, args);
}
public void doClaSignupHook(Account account, ContributorAgreement cla) {
@@ -495,18 +448,14 @@
/**
* Run a hook.
*
- * @param repo repository to run the hook for.
+ * @param project used to open repository to run the hook for.
* @param hook the hook to execute.
* @param args Arguments to use to run the hook.
*/
- private synchronized void runHook(Repository repo, File hook,
+ private synchronized void runHook(Project.NameKey project, File hook,
List<String> args) {
- if (repo != null) {
- if (hook.exists()) {
- hookQueue.execute(new HookTask(repo, hook, args));
- } else {
- repo.close();
- }
+ if (project != null && hook.exists()) {
+ hookQueue.execute(new HookTask(project, hook, args));
}
}
@@ -517,18 +466,19 @@
}
private final class HookTask implements Runnable {
- private final Repository repo;
+ private final Project.NameKey project;
private final File hook;
private final List<String> args;
- private HookTask(Repository repo, File hook, List<String> args) {
- this.repo = repo;
+ private HookTask(Project.NameKey project, File hook, List<String> args) {
+ this.project = project;
this.hook = hook;
this.args = args;
}
@Override
public void run() {
+ Repository repo = null;
try {
final List<String> argv = new ArrayList<String>(1 + args.size());
argv.add(hook.getAbsolutePath());
@@ -536,6 +486,11 @@
final ProcessBuilder pb = new ProcessBuilder(argv);
pb.redirectErrorStream(true);
+
+ if (project != null) {
+ repo = openRepository(project);
+ }
+
if (repo != null) {
pb.directory(repo.getDirectory());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java
new file mode 100644
index 0000000..3447c1f
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java
@@ -0,0 +1,119 @@
+// 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;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ContributorAgreement;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gwtorm.client.OrmException;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.RefUpdate;
+
+import java.util.Map;
+
+/** Invokes hooks on server actions. */
+public interface ChangeHooks {
+ public void addChangeListener(ChangeListener listener, IdentifiedUser user);
+
+ public void removeChangeListener(ChangeListener listener);
+
+ /**
+ * Fire the Patchset Created Hook.
+ *
+ * @param change The change itself.
+ * @param patchSet The Patchset that was created.
+ * @throws OrmException
+ */
+ public void doPatchsetCreatedHook(Change change, PatchSet patchSet,
+ ReviewDb db) throws OrmException;
+
+ /**
+ * Fire the Comment Added Hook.
+ *
+ * @param change The change itself.
+ * @param patchSet The patchset this comment is related to.
+ * @param account The gerrit user who commited the change.
+ * @param comment The comment given.
+ * @param approvals Map of Approval Categories and Scores
+ * @throws OrmException
+ */
+ public void doCommentAddedHook(Change change, Account account,
+ PatchSet patchSet, String comment,
+ Map<ApprovalCategory.Id, ApprovalCategoryValue.Id> approvals, ReviewDb db)
+ throws OrmException;
+
+ /**
+ * Fire the Change Merged Hook.
+ *
+ * @param change The change itself.
+ * @param account The gerrit user who commited the change.
+ * @param patchSet The patchset that was merged.
+ * @throws OrmException
+ */
+ public void doChangeMergedHook(Change change, Account account,
+ PatchSet patchSet, ReviewDb db) throws OrmException;
+
+ /**
+ * Fire the Change Abandoned Hook.
+ *
+ * @param change The change itself.
+ * @param account The gerrit user who abandoned the change.
+ * @param reason Reason for abandoning the change.
+ * @throws OrmException
+ */
+ public void doChangeAbandonedHook(Change change, Account account,
+ String reason, ReviewDb db) throws OrmException;
+
+ /**
+ * Fire the Change Restored Hook.
+ *
+ * @param change The change itself.
+ * @param account The gerrit user who restored the change.
+ * @param reason Reason for restoring the change.
+ * @throws OrmException
+ */
+ public void doChangeRestoreHook(Change change, Account account,
+ String reason, ReviewDb db) throws OrmException;
+
+ /**
+ * Fire the Ref Updated Hook
+ *
+ * @param refName The updated project and branch.
+ * @param refUpdate An actual RefUpdate object
+ * @param account The gerrit user who moved the ref
+ */
+ public void doRefUpdatedHook(Branch.NameKey refName, RefUpdate refUpdate,
+ Account account);
+
+ /**
+ * Fire the Ref Updated Hook
+ *
+ * @param refName The Branch.NameKey of the ref that was updated
+ * @param oldId The ref's old id
+ * @param newId The ref's new id
+ * @param account The gerrit user who moved the ref
+ */
+ public void doRefUpdatedHook(Branch.NameKey refName, ObjectId oldId,
+ ObjectId newId, Account account);
+
+ public void doClaSignupHook(Account account, ContributorAgreement cla);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java b/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java
new file mode 100644
index 0000000..7f61f62
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java
@@ -0,0 +1,81 @@
+// 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;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ContributorAgreement;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.Branch.NameKey;
+import com.google.gerrit.server.IdentifiedUser;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.RefUpdate;
+
+import java.util.Map;
+
+/** Does not invoke hooks. */
+public final class DisabledChangeHooks implements ChangeHooks {
+ @Override
+ public void addChangeListener(ChangeListener listener, IdentifiedUser user) {
+ }
+
+ @Override
+ public void doChangeAbandonedHook(Change change, Account account,
+ String reason, ReviewDb db) {
+ }
+
+ @Override
+ public void doChangeMergedHook(Change change, Account account,
+ PatchSet patchSet, ReviewDb db) {
+ }
+
+ @Override
+ public void doChangeRestoreHook(Change change, Account account,
+ String reason, ReviewDb db) {
+ }
+
+ @Override
+ public void doClaSignupHook(Account account, ContributorAgreement cla) {
+ }
+
+ @Override
+ public void doCommentAddedHook(Change change, Account account,
+ PatchSet patchSet, String comment,
+ Map<ApprovalCategory.Id, ApprovalCategoryValue.Id> approvals, ReviewDb db) {
+ }
+
+ @Override
+ public void doPatchsetCreatedHook(Change change, PatchSet patchSet,
+ ReviewDb db) {
+ }
+
+ @Override
+ public void doRefUpdatedHook(NameKey refName, RefUpdate refUpdate,
+ Account account) {
+ }
+
+ @Override
+ public void doRefUpdatedHook(NameKey refName, ObjectId oldId, ObjectId newId,
+ Account account) {
+ }
+
+ @Override
+ public void removeChangeListener(ChangeListener listener) {
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
new file mode 100644
index 0000000..6997d47
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
@@ -0,0 +1,36 @@
+// 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.server;
+
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gwtorm.client.OrmException;
+
+import java.util.List;
+
+public class ApprovalsUtil {
+ /* Resync the changeOpen status which is cached in the approvals table for
+ performance reasons*/
+ public static void syncChangeStatus(final ReviewDb db, final Change change)
+ throws OrmException {
+ final List<PatchSetApproval> approvals =
+ db.patchSetApprovals().byChange(change.getId()).toList();
+ for (PatchSetApproval a : approvals) {
+ a.cache(change);
+ }
+ db.patchSetApprovals().update(approvals);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
index 9e2d54f..e52c502 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
@@ -14,13 +14,10 @@
package com.google.gerrit.server;
-import static com.google.gerrit.reviewdb.ApprovalCategory.SUBMIT;
-
-import com.google.gerrit.common.ChangeHookRunner;
+import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.ChangeMessage;
import com.google.gerrit.reviewdb.PatchSet;
-import com.google.gerrit.reviewdb.PatchSetApproval;
import com.google.gerrit.reviewdb.PatchSetInfo;
import com.google.gerrit.reviewdb.RevId;
import com.google.gerrit.reviewdb.ReviewDb;
@@ -29,17 +26,14 @@
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeOp;
-import com.google.gerrit.server.git.MergeQueue;
import com.google.gerrit.server.git.ReplicationQueue;
import com.google.gerrit.server.mail.EmailException;
import com.google.gerrit.server.mail.ReplyToChangeSender;
-import com.google.gerrit.server.mail.RestoredSender;
import com.google.gerrit.server.mail.RevertedSender;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gwtorm.client.AtomicUpdate;
import com.google.gwtorm.client.OrmConcurrencyException;
import com.google.gwtorm.client.OrmException;
@@ -51,7 +45,6 @@
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.FooterLine;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -60,7 +53,6 @@
import org.eclipse.jgit.util.NB;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -167,56 +159,10 @@
opFactory.create(change.getDest()).verifyMergeability(change);
}
- public static void submit(final PatchSet.Id patchSetId,
- final IdentifiedUser user, final ReviewDb db,
- final MergeOp.Factory opFactory, final MergeQueue merger)
- throws OrmException {
- final Change.Id changeId = patchSetId.getParentKey();
- final PatchSetApproval approval = createSubmitApproval(patchSetId, user, db);
-
- db.patchSetApprovals().upsert(Collections.singleton(approval));
-
- final Change updatedChange = db.changes().atomicUpdate(changeId,
- new AtomicUpdate<Change>() {
- @Override
- public Change update(Change change) {
- if (change.getStatus() == Change.Status.NEW) {
- change.setStatus(Change.Status.SUBMITTED);
- ChangeUtil.updated(change);
- }
- return change;
- }
- });
-
- if (updatedChange.getStatus() == Change.Status.SUBMITTED) {
- merger.merge(opFactory, updatedChange.getDest());
- }
- }
-
- public static PatchSetApproval createSubmitApproval(
- final PatchSet.Id patchSetId, final IdentifiedUser user, final ReviewDb db
- ) throws OrmException {
- final List<PatchSetApproval> allApprovals =
- new ArrayList<PatchSetApproval>(db.patchSetApprovals().byPatchSet(
- patchSetId).toList());
-
- final PatchSetApproval.Key akey =
- new PatchSetApproval.Key(patchSetId, user.getAccountId(), SUBMIT);
-
- for (final PatchSetApproval approval : allApprovals) {
- if (akey.equals(approval.getKey())) {
- approval.setValue((short) 1);
- approval.setGranted();
- return approval;
- }
- }
- return new PatchSetApproval(akey, (short) 1);
- }
-
public static Change.Id revert(final PatchSet.Id patchSetId,
final IdentifiedUser user, final String message, final ReviewDb db,
final RevertedSender.Factory revertedSenderFactory,
- final ChangeHookRunner hooks, GitRepositoryManager gitManager,
+ final ChangeHooks hooks, GitRepositoryManager gitManager,
final PatchSetInfoFactory patchSetInfoFactory,
final ReplicationQueue replication, PersonIdent myIdent)
throws NoSuchChangeException, EmailException, OrmException,
@@ -317,86 +263,6 @@
}
}
- public static void restore(final PatchSet.Id patchSetId,
- final IdentifiedUser user, final String message, final ReviewDb db,
- final RestoredSender.Factory senderFactory,
- final ChangeHookRunner hooks) throws NoSuchChangeException,
- InvalidChangeOperationException, EmailException, OrmException {
- final Change.Id changeId = patchSetId.getParentKey();
- final PatchSet patch = db.patchSets().get(patchSetId);
- if (patch == null) {
- throw new NoSuchChangeException(changeId);
- }
-
- final ChangeMessage cmsg =
- new ChangeMessage(new ChangeMessage.Key(changeId, ChangeUtil
- .messageUUID(db)), user.getAccountId(), patchSetId);
- final StringBuilder msgBuf =
- new StringBuilder("Patch Set " + patchSetId.get() + ": Restored");
- if (message != null && message.length() > 0) {
- msgBuf.append("\n\n");
- msgBuf.append(message);
- }
- cmsg.setMessage(msgBuf.toString());
-
- final Change updatedChange = db.changes().atomicUpdate(changeId,
- new AtomicUpdate<Change>() {
- @Override
- public Change update(Change change) {
- if (change.getStatus() == Change.Status.ABANDONED
- && change.currentPatchSetId().equals(patchSetId)) {
- change.setStatus(Change.Status.NEW);
- ChangeUtil.updated(change);
- return change;
- } else {
- return null;
- }
- }
- });
-
- updatedChange(db, user, updatedChange, cmsg, senderFactory,
- "Change is not abandoned or patchset is not latest");
-
- hooks.doChangeRestoreHook(updatedChange, user.getAccount(), message, db);
- }
-
- public static void publishDraftPatchSet(final ReviewDb db,
- final PatchSet.Id patchSetId) throws OrmException, NoSuchChangeException{
- final Change.Id changeId = patchSetId.getParentKey();
- final PatchSet patch = db.patchSets().get(patchSetId);
- if (patch == null || !patch.isDraft()) {
- throw new NoSuchChangeException(changeId);
- }
-
- db.patchSets().atomicUpdate(patchSetId, new AtomicUpdate<PatchSet>() {
- @Override
- public PatchSet update(PatchSet patchset) {
- if (patchset.isDraft()) {
- patchset.setDraft(false);
- }
- return null;
- }
- });
-
- final Change change = db.changes().get(changeId);
- if (change.getStatus() == Change.Status.DRAFT) {
- db.changes().atomicUpdate(changeId,
- new AtomicUpdate<Change>() {
- @Override
- public Change update(Change change) {
- if (change.getStatus() == Change.Status.DRAFT
- && change.currentPatchSetId().equals(patchSetId)) {
- change.setStatus(Change.Status.NEW);
- ChangeUtil.updated(change);
- return change;
- } else {
- return null;
- }
- }
- });
- }
- }
-
public static void deleteDraftChange(final PatchSet.Id patchSetId,
GitRepositoryManager gitManager,
final ReplicationQueue replication, final ReviewDb db)
@@ -418,37 +284,7 @@
db.changes().delete(Collections.singleton(change));
}
- public static void deleteDraftPatchSet(final PatchSet.Id patchSetId,
- GitRepositoryManager gitManager,
- final ReplicationQueue replication,
- final PatchSetInfoFactory patchSetInfoFactory,
- final ReviewDb db) throws NoSuchChangeException, OrmException,
- PatchSetInfoNotAvailableException, IOException {
- final Change.Id changeId = patchSetId.getParentKey();
- final Change change = db.changes().get(changeId);
- final PatchSet patch = db.patchSets().get(patchSetId);
-
- deleteOnlyDraftPatchSet(patch, change, gitManager, replication, db);
-
- List<PatchSet> restOfPatches = db.patchSets().byChange(changeId).toList();
- if (restOfPatches.size() == 0) {
- deleteDraftChange(patchSetId, gitManager, replication, db);
- } else {
- PatchSet.Id highestId = null;
- for (PatchSet ps : restOfPatches) {
- if (highestId == null || ps.getPatchSetId() > highestId.get()) {
- highestId = ps.getId();
- }
- }
- if (change.currentPatchSetId().equals(patchSetId)) {
- change.removeLastPatchSetId();
- change.setCurrentPatchSet(patchSetInfoFactory.get(db, change.currPatchSetId()));
- db.changes().update(Collections.singleton(change));
- }
- }
- }
-
- private static void deleteOnlyDraftPatchSet(final PatchSet patch,
+ public static void deleteOnlyDraftPatchSet(final PatchSet patch,
final Change change, GitRepositoryManager gitManager,
final ReplicationQueue replication, final ReviewDb db)
throws NoSuchChangeException, OrmException, IOException {
@@ -497,12 +333,7 @@
}
db.changeMessages().insert(Collections.singleton(cmsg));
- final List<PatchSetApproval> approvals =
- db.patchSetApprovals().byChange(change.getId()).toList();
- for (PatchSetApproval a : approvals) {
- a.cache(change);
- }
- db.patchSetApprovals().update(approvals);
+ ApprovalsUtil.syncChangeStatus(db, change);
// Email the reviewers
final ReplyToChangeSender cm = senderFactory.create(change);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountControl.java
new file mode 100644
index 0000000..0e79c12
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountControl.java
@@ -0,0 +1,143 @@
+// 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.account;
+
+import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.lib.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/** Access control management for one account's access to other accounts. */
+public class AccountControl {
+ private static final Logger log =
+ LoggerFactory.getLogger(AccountControl.class);
+
+ public static class Factory {
+ private final GroupControl.Factory groupControlFactory;
+ private final Provider<CurrentUser> user;
+ private final IdentifiedUser.GenericFactory userFactory;
+ private final AccountVisibility accountVisibility;
+
+ @Inject
+ Factory(final GroupControl.Factory groupControlFactory,
+ final Provider<CurrentUser> user,
+ final IdentifiedUser.GenericFactory userFactory,
+ @GerritServerConfig final Config cfg) {
+ this.groupControlFactory = groupControlFactory;
+ this.user = user;
+ this.userFactory = userFactory;
+
+ AccountVisibility av;
+ if (cfg.getString("accounts", null, "visibility") != null) {
+ av = cfg.getEnum("accounts", null, "visibility", AccountVisibility.ALL);
+ } else {
+ try {
+ av = cfg.getEnum("suggest", null, "accounts", AccountVisibility.ALL);
+ log.warn(String.format(
+ "Using legacy value %s for suggest.accounts;"
+ + " use accounts.visibility=%s instead",
+ av, av));
+ } catch (IllegalArgumentException err) {
+ // If suggest.accounts is a valid boolean, it's a new-style config, and
+ // we should use the default here. Invalid values are caught in
+ // SuggestServiceImpl so we don't worry about them here.
+ av = AccountVisibility.ALL;
+ }
+ }
+ accountVisibility = av;
+ }
+
+ public AccountControl get() {
+ return new AccountControl(groupControlFactory, user.get(), userFactory,
+ accountVisibility);
+ }
+ }
+
+ private final GroupControl.Factory groupControlFactory;
+ private final CurrentUser currentUser;
+ private final IdentifiedUser.GenericFactory userFactory;
+ private final AccountVisibility accountVisibility;
+
+ AccountControl(final GroupControl.Factory groupControlFactory,
+ final CurrentUser currentUser,
+ final IdentifiedUser.GenericFactory userFactory,
+ final AccountVisibility accountVisibility) {
+ this.groupControlFactory = groupControlFactory;
+ this.currentUser = currentUser;
+ this.userFactory = userFactory;
+ this.accountVisibility = accountVisibility;
+ }
+
+ public boolean canSee(final Account otherUser) {
+ // Special case: I can always see myself.
+ if (currentUser instanceof IdentifiedUser
+ && ((IdentifiedUser) currentUser).getAccountId()
+ .equals(otherUser.getId())) {
+ return true;
+ }
+
+ switch (accountVisibility) {
+ case ALL:
+ return true;
+ case SAME_GROUP: {
+ Set<AccountGroup.UUID> usersGroups = groupsOf(otherUser);
+ usersGroups.remove(AccountGroup.ANONYMOUS_USERS);
+ usersGroups.remove(AccountGroup.REGISTERED_USERS);
+ for (AccountGroup.UUID myGroup : currentUser.getEffectiveGroups()) {
+ if (usersGroups.contains(myGroup)) {
+ return true;
+ }
+ }
+ break;
+ }
+ case VISIBLE_GROUP: {
+ Set<AccountGroup.UUID> usersGroups = groupsOf(otherUser);
+ usersGroups.remove(AccountGroup.ANONYMOUS_USERS);
+ usersGroups.remove(AccountGroup.REGISTERED_USERS);
+ for (AccountGroup.UUID usersGroup : usersGroups) {
+ try {
+ if (groupControlFactory.controlFor(usersGroup).isVisible()) {
+ return true;
+ }
+ } catch (NoSuchGroupException e) {
+ continue;
+ }
+ }
+ break;
+ }
+ case NONE:
+ break;
+ default:
+ throw new IllegalStateException("Bad AccountVisibility " + accountVisibility);
+ }
+ return false;
+ }
+
+ private Set<AccountGroup.UUID> groupsOf(Account account) {
+ IdentifiedUser user = userFactory.create(account.getId());
+ return new HashSet<AccountGroup.UUID>(user.getEffectiveGroups());
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountVisibility.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountVisibility.java
new file mode 100644
index 0000000..7452da3
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountVisibility.java
@@ -0,0 +1,33 @@
+// 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.account;
+
+/** Visibility level of other accounts to a given user. */
+public enum AccountVisibility {
+ /** All accounts are visible to all users. */
+ ALL,
+
+ /** Accounts sharing a group with the given user. */
+ SAME_GROUP,
+
+ /** Accounts in a group that is visible to the given user. */
+ VISIBLE_GROUP,
+
+ /**
+ * Other accounts are not visible to the given user unless they are explicitly
+ * collaborating on a change.
+ */
+ NONE;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/Cache.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/Cache.java
index 7159501..7892ea1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/Cache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/Cache.java
@@ -14,9 +14,6 @@
package com.google.gerrit.server.cache;
-import java.util.concurrent.TimeUnit;
-
-
/**
* A fast in-memory and/or on-disk based cache.
*
@@ -35,12 +32,4 @@
/** Remove all cached items. */
public void removeAll();
-
- /**
- * Get the time an element will survive in the cache.
- *
- * @param unit desired units of the return value.
- * @return time an item can live before being purged.
- */
- public long getTimeToLive(TimeUnit unit);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java
index ed596da..3370b08 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 The Android Open Source Project
+// Copyright (C) 2010 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,234 +14,6 @@
package com.google.gerrit.server.cache;
-import static java.util.concurrent.TimeUnit.MINUTES;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import com.google.gerrit.lifecycle.LifecycleListener;
-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.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 CachePool {
- private static final Logger log = LoggerFactory.getLogger(CachePool.class);
-
- public static class Lifecycle implements LifecycleListener {
- private final CachePool cachePool;
-
- @Inject
- Lifecycle(final CachePool 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
- CachePool(@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()) {
- p.bind(manager.getEhcache(p.getName()));
- }
- }
- }
-
- 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;
- }
- }
-
- <K, V> ProxyEhcache 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 ProxyEhcache(n);
- }
- }
-
- 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;
- }
- }
+public interface CachePool {
+ public <K, V> ProxyCache<K, V> register(CacheProvider<K, V> provider);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheProvider.java
index 0ba424f2..1fa047b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheProvider.java
@@ -22,11 +22,9 @@
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
-import net.sf.ehcache.Ehcache;
-
import java.util.concurrent.TimeUnit;
-final class CacheProvider<K, V> implements Provider<Cache<K, V>>,
+public final class CacheProvider<K, V> implements Provider<Cache<K, V>>,
NamedCacheBinding<K, V>, UnnamedCacheBinding<K, V> {
private final CacheModule module;
private final boolean disk;
@@ -35,7 +33,7 @@
private long maxAge;
private EvictionPolicy evictionPolicy;
private String cacheName;
- private ProxyEhcache cache;
+ private ProxyCache<K, V> cache;
private Provider<EntryCreator<K, V>> entryCreator;
CacheProvider(final boolean disk, CacheModule module) {
@@ -56,34 +54,41 @@
this.cache = pool.register(this);
}
- void bind(final Ehcache ehcache) {
- cache.bind(ehcache);
+ public void bind(Cache<K, V> impl) {
+ if (cache == null) {
+ throw new ProvisionException("Cache was never registered");
+ }
+ cache.bind(impl);
}
- String getName() {
+ public EntryCreator<K, V> getEntryCreator() {
+ return entryCreator != null ? entryCreator.get() : null;
+ }
+
+ public String getName() {
if (cacheName == null) {
throw new ProvisionException("Cache has no name");
}
return cacheName;
}
- boolean disk() {
+ public boolean disk() {
return disk;
}
- int memoryLimit() {
+ public int memoryLimit() {
return memoryLimit;
}
- int diskLimit() {
+ public int diskLimit() {
return diskLimit;
}
- long maxAge() {
+ public long maxAge() {
return maxAge;
}
- EvictionPolicy evictionPolicy() {
+ public EvictionPolicy evictionPolicy() {
return evictionPolicy;
}
@@ -133,9 +138,6 @@
if (cache == null) {
throw new ProvisionException("Cache \"" + cacheName + "\" not available");
}
- if (entryCreator != null) {
- return new PopulatingCache<K, V>(cache, entryCreator.get());
- }
- return new SimpleCache<K, V>(cache);
+ return cache;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/ConcurrentHashMapCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/ConcurrentHashMapCache.java
index 268fe2b..bafdc49 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/ConcurrentHashMapCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/ConcurrentHashMapCache.java
@@ -15,7 +15,6 @@
package com.google.gerrit.server.cache;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
/**
* An infinitely sized cache backed by java.util.ConcurrentHashMap.
@@ -46,9 +45,4 @@
public void removeAll() {
map.clear();
}
-
- @Override
- public long getTimeToLive(TimeUnit unit) {
- return 0;
- }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/ProxyCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/ProxyCache.java
new file mode 100644
index 0000000..c1b0292
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/ProxyCache.java
@@ -0,0 +1,40 @@
+// Copyright (C) 2010 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;
+
+/** Proxy around a cache which has not yet been created. */
+public final class ProxyCache<K, V> implements Cache<K, V> {
+ private volatile Cache<K, V> self;
+
+ public void bind(Cache<K, V> self) {
+ this.self = self;
+ }
+
+ public V get(K key) {
+ return self.get(key);
+ }
+
+ public void put(K key, V value) {
+ self.put(key, value);
+ }
+
+ public void remove(K key) {
+ self.remove(key);
+ }
+
+ public void removeAll() {
+ self.removeAll();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/ProxyEhcache.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/ProxyEhcache.java
deleted file mode 100644
index bcead63..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/ProxyEhcache.java
+++ /dev/null
@@ -1,393 +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.server.cache;
-
-import net.sf.ehcache.CacheException;
-import net.sf.ehcache.CacheManager;
-import net.sf.ehcache.Ehcache;
-import net.sf.ehcache.Element;
-import net.sf.ehcache.Statistics;
-import net.sf.ehcache.Status;
-import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
-import net.sf.ehcache.config.CacheConfiguration;
-import net.sf.ehcache.event.RegisteredEventListeners;
-import net.sf.ehcache.exceptionhandler.CacheExceptionHandler;
-import net.sf.ehcache.extension.CacheExtension;
-import net.sf.ehcache.loader.CacheLoader;
-import net.sf.ehcache.statistics.CacheUsageListener;
-import net.sf.ehcache.statistics.LiveCacheStatistics;
-import net.sf.ehcache.statistics.sampled.SampledCacheStatistics;
-
-import java.io.Serializable;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/** Proxy around a cache which has not yet been created. */
-final class ProxyEhcache implements Ehcache {
- private final String cacheName;
- private volatile Ehcache self;
-
- ProxyEhcache(final String cacheName) {
- this.cacheName = cacheName;
- }
-
- void bind(final Ehcache self) {
- this.self = self;
- }
-
- private Ehcache self() {
- return self;
- }
-
- @Override
- public Object clone() throws CloneNotSupportedException {
- throw new CloneNotSupportedException();
- }
-
- @Override
- public String getName() {
- return cacheName;
- }
-
- @Override
- public void setName(String name) {
- throw new UnsupportedOperationException();
- }
-
- //
- // Everything else delegates through self.
- //
-
- public void bootstrap() {
- self().bootstrap();
- }
-
- public long calculateInMemorySize() throws IllegalStateException,
- CacheException {
- return self().calculateInMemorySize();
- }
-
- public void clearStatistics() {
- self().clearStatistics();
- }
-
- public void dispose() throws IllegalStateException {
- self().dispose();
- }
-
- public void evictExpiredElements() {
- self().evictExpiredElements();
- }
-
- public void flush() throws IllegalStateException, CacheException {
- self().flush();
- }
-
- public Element get(Object key) throws IllegalStateException, CacheException {
- return self().get(key);
- }
-
- public Element get(Serializable key) throws IllegalStateException,
- CacheException {
- return self().get(key);
- }
-
- @SuppressWarnings("rawtypes")
- public Map getAllWithLoader(Collection keys, Object loaderArgument)
- throws CacheException {
- return self().getAllWithLoader(keys, loaderArgument);
- }
-
- public float getAverageGetTime() {
- return self().getAverageGetTime();
- }
-
- public BootstrapCacheLoader getBootstrapCacheLoader() {
- return self().getBootstrapCacheLoader();
- }
-
- public CacheConfiguration getCacheConfiguration() {
- if (self == null) {
- // In Ehcache 1.7, BlockingCache wants to ask us if we are
- // clustered using Terracotta. Unfortunately it is too early
- // to know for certain as the caches have not actually been
- // created or configured.
- //
- return new CacheConfiguration();
- }
- return self().getCacheConfiguration();
- }
-
- public RegisteredEventListeners getCacheEventNotificationService() {
- return self().getCacheEventNotificationService();
- }
-
- public CacheExceptionHandler getCacheExceptionHandler() {
- return self().getCacheExceptionHandler();
- }
-
- public CacheManager getCacheManager() {
- return self().getCacheManager();
- }
-
- public int getDiskStoreSize() throws IllegalStateException {
- return self().getDiskStoreSize();
- }
-
- public String getGuid() {
- return self().getGuid();
- }
-
- @SuppressWarnings("rawtypes")
- public List getKeys() throws IllegalStateException, CacheException {
- return self().getKeys();
- }
-
- @SuppressWarnings("rawtypes")
- public List getKeysNoDuplicateCheck() throws IllegalStateException {
- return self().getKeysNoDuplicateCheck();
- }
-
- @SuppressWarnings("rawtypes")
- public List getKeysWithExpiryCheck() throws IllegalStateException,
- CacheException {
- return self().getKeysWithExpiryCheck();
- }
-
- public long getMemoryStoreSize() throws IllegalStateException {
- return self().getMemoryStoreSize();
- }
-
- public Element getQuiet(Object key) throws IllegalStateException,
- CacheException {
- return self().getQuiet(key);
- }
-
- public Element getQuiet(Serializable key) throws IllegalStateException,
- CacheException {
- return self().getQuiet(key);
- }
-
- public List<CacheExtension> getRegisteredCacheExtensions() {
- return self().getRegisteredCacheExtensions();
- }
-
- public List<CacheLoader> getRegisteredCacheLoaders() {
- return self().getRegisteredCacheLoaders();
- }
-
- public int getSize() throws IllegalStateException, CacheException {
- return self().getSize();
- }
-
- public Statistics getStatistics() throws IllegalStateException {
- return self().getStatistics();
- }
-
- public int getStatisticsAccuracy() {
- return self().getStatisticsAccuracy();
- }
-
- public Status getStatus() {
- return self().getStatus();
- }
-
- public Element getWithLoader(Object key, CacheLoader loader,
- Object loaderArgument) throws CacheException {
- return self().getWithLoader(key, loader, loaderArgument);
- }
-
- public void initialise() {
- self().initialise();
- }
-
- public boolean isDisabled() {
- return self().isDisabled();
- }
-
- public boolean isElementInMemory(Object key) {
- return self().isElementInMemory(key);
- }
-
- public boolean isElementInMemory(Serializable key) {
- return self().isElementInMemory(key);
- }
-
- public boolean isElementOnDisk(Object key) {
- return self().isElementOnDisk(key);
- }
-
- public boolean isElementOnDisk(Serializable key) {
- return self().isElementOnDisk(key);
- }
-
- public boolean isExpired(Element element) throws IllegalStateException,
- NullPointerException {
- return self().isExpired(element);
- }
-
- public boolean isKeyInCache(Object key) {
- return self().isKeyInCache(key);
- }
-
- public boolean isValueInCache(Object value) {
- return self().isValueInCache(value);
- }
-
- public void load(Object key) throws CacheException {
- self().load(key);
- }
-
- @SuppressWarnings("rawtypes")
- public void loadAll(Collection keys, Object argument) throws CacheException {
- self().loadAll(keys, argument);
- }
-
- public void put(Element element, boolean doNotNotifyCacheReplicators)
- throws IllegalArgumentException, IllegalStateException, CacheException {
- self().put(element, doNotNotifyCacheReplicators);
- }
-
- public void put(Element element) throws IllegalArgumentException,
- IllegalStateException, CacheException {
- self().put(element);
- }
-
- public void putQuiet(Element element) throws IllegalArgumentException,
- IllegalStateException, CacheException {
- self().putQuiet(element);
- }
-
- public void registerCacheExtension(CacheExtension cacheExtension) {
- self().registerCacheExtension(cacheExtension);
- }
-
- public void registerCacheLoader(CacheLoader cacheLoader) {
- self().registerCacheLoader(cacheLoader);
- }
-
- public boolean remove(Object key, boolean doNotNotifyCacheReplicators)
- throws IllegalStateException {
- return self().remove(key, doNotNotifyCacheReplicators);
- }
-
- public boolean remove(Object key) throws IllegalStateException {
- return self().remove(key);
- }
-
- public boolean remove(Serializable key, boolean doNotNotifyCacheReplicators)
- throws IllegalStateException {
- return self().remove(key, doNotNotifyCacheReplicators);
- }
-
- public boolean remove(Serializable key) throws IllegalStateException {
- return self().remove(key);
- }
-
- public void removeAll() throws IllegalStateException, CacheException {
- self().removeAll();
- }
-
- public void removeAll(boolean doNotNotifyCacheReplicators)
- throws IllegalStateException, CacheException {
- self().removeAll(doNotNotifyCacheReplicators);
- }
-
- public boolean removeQuiet(Object key) throws IllegalStateException {
- return self().removeQuiet(key);
- }
-
- public boolean removeQuiet(Serializable key) throws IllegalStateException {
- return self().removeQuiet(key);
- }
-
- public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader)
- throws CacheException {
- self().setBootstrapCacheLoader(bootstrapCacheLoader);
- }
-
- public void setCacheExceptionHandler(
- CacheExceptionHandler cacheExceptionHandler) {
- self().setCacheExceptionHandler(cacheExceptionHandler);
- }
-
- public void setCacheManager(CacheManager cacheManager) {
- self().setCacheManager(cacheManager);
- }
-
- public void setDisabled(boolean disabled) {
- self().setDisabled(disabled);
- }
-
- public void setDiskStorePath(String diskStorePath) throws CacheException {
- self().setDiskStorePath(diskStorePath);
- }
-
- public void setStatisticsAccuracy(int statisticsAccuracy) {
- self().setStatisticsAccuracy(statisticsAccuracy);
- }
-
- public void unregisterCacheExtension(CacheExtension cacheExtension) {
- self().unregisterCacheExtension(cacheExtension);
- }
-
- public void unregisterCacheLoader(CacheLoader cacheLoader) {
- self().unregisterCacheLoader(cacheLoader);
- }
-
- public Object getInternalContext() {
- return self.getInternalContext();
- }
-
- public LiveCacheStatistics getLiveCacheStatistics() throws IllegalStateException {
- return self.getLiveCacheStatistics();
- }
-
- public SampledCacheStatistics getSampledCacheStatistics() {
- return self.getSampledCacheStatistics();
- }
-
- public int getSizeBasedOnAccuracy(int statisticsAccuracy) throws IllegalArgumentException,
- IllegalStateException, CacheException {
- return self.getSizeBasedOnAccuracy(statisticsAccuracy);
- }
-
- public boolean isSampledStatisticsEnabled() {
- return self.isSampledStatisticsEnabled();
- }
-
- public boolean isStatisticsEnabled() {
- return self.isStatisticsEnabled();
- }
-
- public void registerCacheUsageListener(CacheUsageListener cacheUsageListener)
- throws IllegalStateException {
- self.registerCacheUsageListener(cacheUsageListener);
- }
-
- public void removeCacheUsageListener(CacheUsageListener cacheUsageListener)
- throws IllegalStateException {
- self.removeCacheUsageListener(cacheUsageListener);
- }
-
- public void setSampledStatisticsEnabled(boolean enableStatistics) {
- self.setSampledStatisticsEnabled(enableStatistics);
- }
-
- public void setStatisticsEnabled(boolean enableStatistics) {
- self.setStatisticsEnabled(enableStatistics);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/AbandonChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/AbandonChange.java
index 775e738..cc111e6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/AbandonChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/AbandonChange.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.changedetail;
-import com.google.gerrit.common.ChangeHookRunner;
+import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.common.data.ReviewResult;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.ChangeMessage;
@@ -45,7 +45,7 @@
private final ChangeControl.Factory changeControlFactory;
private final ReviewDb db;
private final IdentifiedUser currentUser;
- private final ChangeHookRunner hooks;
+ private final ChangeHooks hooks;
private final PatchSet.Id patchSetId;
private final String changeComment;
@@ -53,7 +53,7 @@
@Inject
AbandonChange(final AbandonedSender.Factory abandonedSenderFactory,
final ChangeControl.Factory changeControlFactory, final ReviewDb db,
- final IdentifiedUser currentUser, final ChangeHookRunner hooks,
+ final IdentifiedUser currentUser, final ChangeHooks hooks,
@Assisted final PatchSet.Id patchSetId,
@Assisted final String changeComment) {
this.abandonedSenderFactory = abandonedSenderFactory;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/DeleteDraftPatchSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/DeleteDraftPatchSet.java
new file mode 100644
index 0000000..761b765
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/DeleteDraftPatchSet.java
@@ -0,0 +1,125 @@
+// 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.server.changedetail;
+
+import com.google.gerrit.common.data.ReviewResult;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.ReplicationQueue;
+import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+public class DeleteDraftPatchSet implements Callable<ReviewResult> {
+
+ public interface Factory {
+ DeleteDraftPatchSet create(PatchSet.Id patchSetId);
+ }
+
+ private final ChangeControl.Factory changeControlFactory;
+ private final ReviewDb db;
+ private final GitRepositoryManager gitManager;
+ private final ReplicationQueue replication;
+ private final PatchSetInfoFactory patchSetInfoFactory;
+
+ private final PatchSet.Id patchSetId;
+
+ @Inject
+ DeleteDraftPatchSet(ChangeControl.Factory changeControlFactory,
+ ReviewDb db, GitRepositoryManager gitManager,
+ ReplicationQueue replication, PatchSetInfoFactory patchSetInfoFactory,
+ @Assisted final PatchSet.Id patchSetId) {
+ this.changeControlFactory = changeControlFactory;
+ this.db = db;
+ this.gitManager = gitManager;
+ this.replication = replication;
+ this.patchSetInfoFactory = patchSetInfoFactory;
+
+ this.patchSetId = patchSetId;
+ }
+
+ @Override
+ public ReviewResult call() throws NoSuchChangeException, OrmException {
+ final ReviewResult result = new ReviewResult();
+
+ final Change.Id changeId = patchSetId.getParentKey();
+ result.setChangeId(changeId);
+ final ChangeControl control = changeControlFactory.validateFor(changeId);
+ final PatchSet patch = db.patchSets().get(patchSetId);
+ if (patch == null) {
+ throw new NoSuchChangeException(changeId);
+ }
+ if (!patch.isDraft()) {
+ result.addError(new ReviewResult.Error(
+ ReviewResult.Error.Type.NOT_A_DRAFT));
+ return result;
+ }
+
+ if (!control.canDeleteDraft(db)) {
+ result.addError(new ReviewResult.Error(
+ ReviewResult.Error.Type.DELETE_NOT_PERMITTED));
+ return result;
+ }
+ final Change change = control.getChange();
+
+ try {
+ ChangeUtil.deleteOnlyDraftPatchSet(patch, change, gitManager, replication, db);
+ } catch (IOException e) {
+ result.addError(new ReviewResult.Error(
+ ReviewResult.Error.Type.GIT_ERROR, e.getMessage()));
+ }
+
+ List<PatchSet> restOfPatches = db.patchSets().byChange(changeId).toList();
+ if (restOfPatches.size() == 0) {
+ try {
+ ChangeUtil.deleteDraftChange(patchSetId, gitManager, replication, db);
+ result.setChangeId(null);
+ } catch (IOException e) {
+ result.addError(new ReviewResult.Error(
+ ReviewResult.Error.Type.GIT_ERROR, e.getMessage()));
+ }
+ } else {
+ PatchSet.Id highestId = null;
+ for (PatchSet ps : restOfPatches) {
+ if (highestId == null || ps.getPatchSetId() > highestId.get()) {
+ highestId = ps.getId();
+ }
+ }
+ if (change.currentPatchSetId().equals(patchSetId)) {
+ change.removeLastPatchSetId();
+ try {
+ change.setCurrentPatchSet(patchSetInfoFactory.get(db, change.currPatchSetId()));
+ } catch (PatchSetInfoNotAvailableException e) {
+ throw new NoSuchChangeException(changeId);
+ }
+ db.changes().update(Collections.singleton(change));
+ }
+ }
+ return result;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/PublishDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/PublishDraft.java
new file mode 100644
index 0000000..c24c73c
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/PublishDraft.java
@@ -0,0 +1,103 @@
+// 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.server.changedetail;
+
+import com.google.gerrit.common.data.ReviewResult;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gwtorm.client.AtomicUpdate;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.concurrent.Callable;
+
+public class PublishDraft implements Callable<ReviewResult> {
+
+ public interface Factory {
+ PublishDraft create(PatchSet.Id patchSetId);
+ }
+
+ private final ChangeControl.Factory changeControlFactory;
+ private final ReviewDb db;
+
+ private final PatchSet.Id patchSetId;
+
+ @Inject
+ PublishDraft(ChangeControl.Factory changeControlFactory,
+ ReviewDb db, @Assisted final PatchSet.Id patchSetId) {
+ this.changeControlFactory = changeControlFactory;
+ this.db = db;
+
+ this.patchSetId = patchSetId;
+ }
+
+ @Override
+ public ReviewResult call() throws NoSuchChangeException, OrmException {
+ final ReviewResult result = new ReviewResult();
+
+ final Change.Id changeId = patchSetId.getParentKey();
+ result.setChangeId(changeId);
+ final ChangeControl control = changeControlFactory.validateFor(changeId);
+ final PatchSet patch = db.patchSets().get(patchSetId);
+ if (patch == null) {
+ throw new NoSuchChangeException(changeId);
+ }
+ if (!patch.isDraft()) {
+ result.addError(new ReviewResult.Error(
+ ReviewResult.Error.Type.NOT_A_DRAFT));
+ return result;
+ }
+
+ if (!control.canPublish(db)) {
+ result.addError(new ReviewResult.Error(
+ ReviewResult.Error.Type.PUBLISH_NOT_PERMITTED));
+ } else {
+ db.patchSets().atomicUpdate(patchSetId, new AtomicUpdate<PatchSet>() {
+ @Override
+ public PatchSet update(PatchSet patchset) {
+ if (patchset.isDraft()) {
+ patchset.setDraft(false);
+ }
+ return null;
+ }
+ });
+
+ final Change change = db.changes().get(changeId);
+ if (change.getStatus() == Change.Status.DRAFT) {
+ db.changes().atomicUpdate(changeId,
+ new AtomicUpdate<Change>() {
+ @Override
+ public Change update(Change change) {
+ if (change.getStatus() == Change.Status.DRAFT) {
+ change.setStatus(Change.Status.NEW);
+ ChangeUtil.updated(change);
+ return change;
+ } else {
+ return null;
+ }
+ }
+ });
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RestoreChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RestoreChange.java
new file mode 100644
index 0000000..5df973b
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RestoreChange.java
@@ -0,0 +1,123 @@
+// 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.changedetail;
+
+import com.google.gerrit.common.ChangeHooks;
+import com.google.gerrit.common.data.ReviewResult;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ChangeMessage;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.mail.RestoredSender;
+import com.google.gerrit.server.mail.EmailException;
+import com.google.gerrit.server.project.InvalidChangeOperationException;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gwtorm.client.AtomicUpdate;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.concurrent.Callable;
+
+public class RestoreChange implements Callable<ReviewResult> {
+
+ public interface Factory {
+ RestoreChange create(PatchSet.Id patchSetId, String changeComment);
+ }
+
+ private final RestoredSender.Factory restoredSenderFactory;
+ private final ChangeControl.Factory changeControlFactory;
+ private final ReviewDb db;
+ private final IdentifiedUser currentUser;
+ private final ChangeHooks hooks;
+
+ private final PatchSet.Id patchSetId;
+ private final String changeComment;
+
+ @Inject
+ RestoreChange(final RestoredSender.Factory restoredSenderFactory,
+ final ChangeControl.Factory changeControlFactory, final ReviewDb db,
+ final IdentifiedUser currentUser, final ChangeHooks hooks,
+ @Assisted final PatchSet.Id patchSetId,
+ @Assisted final String changeComment) {
+ this.restoredSenderFactory = restoredSenderFactory;
+ this.changeControlFactory = changeControlFactory;
+ this.db = db;
+ this.currentUser = currentUser;
+ this.hooks = hooks;
+
+ this.patchSetId = patchSetId;
+ this.changeComment = changeComment;
+ }
+
+ @Override
+ public ReviewResult call() throws EmailException,
+ InvalidChangeOperationException, NoSuchChangeException, OrmException {
+ final ReviewResult result = new ReviewResult();
+
+ final Change.Id changeId = patchSetId.getParentKey();
+ result.setChangeId(changeId);
+ final ChangeControl control = changeControlFactory.validateFor(changeId);
+ final PatchSet patch = db.patchSets().get(patchSetId);
+ if (!control.canRestore()) {
+ result.addError(new ReviewResult.Error(
+ ReviewResult.Error.Type.RESTORE_NOT_PERMITTED));
+ } else if (patch == null) {
+ throw new NoSuchChangeException(changeId);
+ } else {
+
+ // Create a message to accompany the restored change
+ final ChangeMessage cmsg =
+ new ChangeMessage(new ChangeMessage.Key(changeId, ChangeUtil
+ .messageUUID(db)), currentUser.getAccountId(), patchSetId);
+ final StringBuilder msgBuf =
+ new StringBuilder("Patch Set " + patchSetId.get() + ": Restored");
+ if (changeComment != null && changeComment.length() > 0) {
+ msgBuf.append("\n\n");
+ msgBuf.append(changeComment);
+ }
+ cmsg.setMessage(msgBuf.toString());
+
+ // Restore the change
+ final Change updatedChange = db.changes().atomicUpdate(changeId,
+ new AtomicUpdate<Change>() {
+ @Override
+ public Change update(Change change) {
+ if (change.getStatus() == Change.Status.ABANDONED
+ && change.currentPatchSetId().equals(patchSetId)) {
+ change.setStatus(Change.Status.NEW);
+ ChangeUtil.updated(change);
+ return change;
+ } else {
+ return null;
+ }
+ }
+ });
+
+ ChangeUtil.updatedChange(
+ db, currentUser, updatedChange, cmsg, restoredSenderFactory,
+ "Change is not abandoned or patchset is not latest");
+
+ hooks.doChangeRestoreHook(updatedChange, currentUser.getAccount(),
+ changeComment, db);
+ }
+
+ return result;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/Submit.java b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/Submit.java
new file mode 100644
index 0000000..813b92a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/Submit.java
@@ -0,0 +1,189 @@
+// 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.changedetail;
+
+import static com.google.gerrit.reviewdb.ApprovalCategory.SUBMIT;
+
+import com.google.gerrit.common.data.ReviewResult;
+import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.git.MergeOp;
+import com.google.gerrit.server.git.MergeQueue;
+import com.google.gerrit.server.project.InvalidChangeOperationException;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gwtorm.client.AtomicUpdate;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.concurrent.Callable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class Submit implements Callable<ReviewResult> {
+
+ public interface Factory {
+ Submit create(PatchSet.Id patchSetId);
+ }
+
+ private final ChangeControl.Factory changeControlFactory;
+ private final MergeOp.Factory opFactory;
+ private final MergeQueue merger;
+ private final ReviewDb db;
+ private final IdentifiedUser currentUser;
+
+ private final PatchSet.Id patchSetId;
+
+ @Inject
+ Submit(final ChangeControl.Factory changeControlFactory,
+ final MergeOp.Factory opFactory, final MergeQueue merger,
+ final ReviewDb db, final IdentifiedUser currentUser,
+ @Assisted final PatchSet.Id patchSetId) {
+ this.changeControlFactory = changeControlFactory;
+ this.opFactory = opFactory;
+ this.merger = merger;
+ this.db = db;
+ this.currentUser = currentUser;
+
+ this.patchSetId = patchSetId;
+ }
+
+ @Override
+ public ReviewResult call() throws IllegalStateException,
+ InvalidChangeOperationException, NoSuchChangeException, OrmException {
+ final ReviewResult result = new ReviewResult();
+
+ final PatchSet patch = db.patchSets().get(patchSetId);
+ final Change.Id changeId = patchSetId.getParentKey();
+ final ChangeControl control = changeControlFactory.validateFor(changeId);
+ result.setChangeId(changeId);
+ if (patch == null) {
+ throw new NoSuchChangeException(changeId);
+ }
+
+ List<SubmitRecord> submitResult = control.canSubmit(db, patchSetId);
+ if (submitResult.isEmpty()) {
+ throw new IllegalStateException(
+ "ChangeControl.canSubmit returned empty list");
+ }
+
+ for (SubmitRecord submitRecord : submitResult) {
+ switch (submitRecord.status) {
+ case OK:
+ if (!control.getRefControl().canSubmit()) {
+ result.addError(new ReviewResult.Error(
+ ReviewResult.Error.Type.SUBMIT_NOT_PERMITTED));
+ }
+ break;
+
+ case NOT_READY:
+ StringBuilder errMsg = new StringBuilder();
+ for (SubmitRecord.Label lbl : submitRecord.labels) {
+ switch (lbl.status) {
+ case OK:
+ break;
+
+ case REJECT:
+ if (errMsg.length() > 0) errMsg.append("; ");
+ errMsg.append("change " + changeId + ": blocked by "
+ + lbl.label);
+ break;
+
+ case NEED:
+ if (errMsg.length() > 0) errMsg.append("; ");
+ errMsg.append("change " + changeId + ": needs " + lbl.label);
+ break;
+
+ case IMPOSSIBLE:
+ if (errMsg.length() > 0) errMsg.append("; ");
+ errMsg.append("change " + changeId + ": needs " + lbl.label
+ + " (check project access)");
+ break;
+
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported SubmitRecord.Label.status (" + lbl.status
+ + ")");
+ }
+ }
+ result.addError(new ReviewResult.Error(
+ ReviewResult.Error.Type.SUBMIT_NOT_READY, errMsg.toString()));
+ break;
+
+ case CLOSED:
+ result.addError(new ReviewResult.Error(
+ ReviewResult.Error.Type.CHANGE_IS_CLOSED));
+ break;
+
+ case RULE_ERROR:
+ result.addError(new ReviewResult.Error(
+ ReviewResult.Error.Type.RULE_ERROR,
+ submitResult.get(0).errorMessage));
+ break;
+
+ default:
+ throw new IllegalStateException(
+ "Unsupported SubmitRecord.status + (" + submitRecord.status
+ + ")");
+ }
+ }
+
+ // Submit the change if we can
+ if (result.getErrors().isEmpty()) {
+ final List<PatchSetApproval> allApprovals =
+ new ArrayList<PatchSetApproval>(db.patchSetApprovals().byPatchSet(
+ patchSetId).toList());
+
+ final PatchSetApproval.Key akey =
+ new PatchSetApproval.Key(patchSetId, currentUser.getAccountId(),
+ SUBMIT);
+
+ PatchSetApproval approval = new PatchSetApproval(akey, (short) 1);
+ for (final PatchSetApproval candidateApproval : allApprovals) {
+ if (akey.equals(candidateApproval.getKey())) {
+ candidateApproval.setValue((short) 1);
+ candidateApproval.setGranted();
+ approval = candidateApproval;
+ break;
+ }
+ }
+ db.patchSetApprovals().upsert(Collections.singleton(approval));
+
+ final Change updatedChange = db.changes().atomicUpdate(changeId,
+ new AtomicUpdate<Change>() {
+ @Override
+ public Change update(Change change) {
+ if (change.getStatus() == Change.Status.NEW) {
+ change.setStatus(Change.Status.SUBMITTED);
+ ChangeUtil.updated(change);
+ }
+ return change;
+ }
+ });
+
+ if (updatedChange.getStatus() == Change.Status.SUBMITTED) {
+ merger.merge(opFactory, updatedChange.getDest());
+ }
+ }
+ return result;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
index 88639b7..fc189dd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
@@ -61,7 +61,7 @@
if (key != null && !key.isEmpty()) {
int age = (int) ConfigUtil.getTimeUnit(cfg,
"auth", null, "maxRegisterEmailTokenAge",
- TimeUnit.SECONDS.convert(5, TimeUnit.DAYS),
+ TimeUnit.SECONDS.convert(12, TimeUnit.HOURS),
TimeUnit.SECONDS);
emailReg = new SignedToken(age, key);
} else {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 227f4a7..171d719 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -17,7 +17,6 @@
import static com.google.inject.Scopes.SINGLETON;
import com.google.gerrit.common.data.ApprovalTypes;
-import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.reviewdb.AuthType;
import com.google.gerrit.rules.PrologModule;
import com.google.gerrit.rules.RulesCache;
@@ -36,7 +35,6 @@
import com.google.gerrit.server.account.GroupInfoCacheFactory;
import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.auth.ldap.LdapModule;
-import com.google.gerrit.server.cache.CachePool;
import com.google.gerrit.server.events.EventFactory;
import com.google.gerrit.server.git.ChangeMergeQueue;
import com.google.gerrit.server.git.GitModule;
@@ -101,7 +99,6 @@
SINGLETON);
bind(IdGenerator.class);
- bind(CachePool.class);
bind(RulesCache.class);
install(AccountByEmailCacheImpl.module());
install(AccountCacheImpl.module());
@@ -129,6 +126,7 @@
factory(SecureCredentialsProvider.Factory.class);
factory(PushAllProjectsOp.Factory.class);
+ bind(ChangeMergeQueue.class).in(SINGLETON);
bind(MergeQueue.class).to(ChangeMergeQueue.class).in(SINGLETON);
factory(ReloadSubmitQueueOp.Factory.class);
@@ -144,12 +142,5 @@
bind(ProjectControl.GenericFactory.class);
factory(FunctionState.Factory.class);
factory(ReplicationUser.Factory.class);
-
- install(new LifecycleModule() {
- @Override
- protected void configure() {
- listener().to(CachePool.Lifecycle.class);
- }
- });
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
index 179660e..7bc1bf8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
@@ -20,6 +20,7 @@
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.RequestCleanup;
+import com.google.gerrit.server.account.AccountControl;
import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.account.GroupDetailFactory;
@@ -28,6 +29,10 @@
import com.google.gerrit.server.account.PerformRenameGroup;
import com.google.gerrit.server.account.VisibleGroups;
import com.google.gerrit.server.changedetail.AbandonChange;
+import com.google.gerrit.server.changedetail.DeleteDraftPatchSet;
+import com.google.gerrit.server.changedetail.PublishDraft;
+import com.google.gerrit.server.changedetail.RestoreChange;
+import com.google.gerrit.server.changedetail.Submit;
import com.google.gerrit.server.git.CreateCodeReviewNotes;
import com.google.gerrit.server.git.MergeOp;
import com.google.gerrit.server.git.MetaDataUpdate;
@@ -39,7 +44,6 @@
import com.google.gerrit.server.mail.CreateChangeSender;
import com.google.gerrit.server.mail.MergeFailSender;
import com.google.gerrit.server.mail.MergedSender;
-import com.google.gerrit.server.mail.RegisterNewEmailSender;
import com.google.gerrit.server.mail.ReplacePatchSetSender;
import com.google.gerrit.server.mail.RestoredSender;
import com.google.gerrit.server.mail.RevertedSender;
@@ -50,6 +54,7 @@
import com.google.gerrit.server.project.CreateProject;
import com.google.gerrit.server.project.PerRequestProjectControlCache;
import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.SuggestParentCandidates;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.ChangeQueryRewriter;
import com.google.inject.servlet.RequestScoped;
@@ -71,6 +76,7 @@
bind(ChangeControl.Factory.class).in(SINGLETON);
bind(GroupControl.Factory.class).in(SINGLETON);
bind(ProjectControl.Factory.class).in(SINGLETON);
+ bind(AccountControl.Factory.class).in(SINGLETON);
factory(ChangeQueryBuilder.Factory.class);
factory(ReceiveCommits.Factory.class);
@@ -85,21 +91,25 @@
factory(AddReviewer.Factory.class);
factory(AddReviewerSender.Factory.class);
factory(CreateChangeSender.Factory.class);
+ factory(DeleteDraftPatchSet.Factory.class);
factory(PublishComments.Factory.class);
+ factory(PublishDraft.Factory.class);
factory(ReplacePatchSetSender.Factory.class);
factory(AbandonedSender.Factory.class);
factory(RemoveReviewer.Factory.class);
+ factory(RestoreChange.Factory.class);
factory(RestoredSender.Factory.class);
factory(RevertedSender.Factory.class);
factory(CommentSender.Factory.class);
factory(MergedSender.Factory.class);
factory(MergeFailSender.Factory.class);
- factory(RegisterNewEmailSender.Factory.class);
factory(PerformCreateGroup.Factory.class);
factory(PerformRenameGroup.Factory.class);
factory(VisibleGroups.Factory.class);
factory(GroupDetailFactory.Factory.class);
factory(GroupMembers.Factory.class);
factory(CreateProject.Factory.class);
+ factory(Submit.Factory.class);
+ factory(SuggestParentCandidates.Factory.class);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java
index cbe6594..0bcd37b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java
@@ -29,6 +29,7 @@
import com.google.inject.Injector;
import com.google.inject.OutOfScopeException;
import com.google.inject.Provider;
+import com.google.inject.Singleton;
import com.google.inject.servlet.RequestScoped;
import com.jcraft.jsch.HostKey;
@@ -43,6 +44,7 @@
import java.util.Map;
import java.util.concurrent.TimeUnit;
+@Singleton
public class ChangeMergeQueue implements MergeQueue {
private static final Logger log =
LoggerFactory.getLogger(ChangeMergeQueue.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
index 5d5cf34..b08b149 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -17,7 +17,7 @@
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
-import com.google.gerrit.common.ChangeHookRunner;
+import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.common.data.ApprovalType;
import com.google.gerrit.common.data.ApprovalTypes;
import com.google.gerrit.common.data.Capable;
@@ -150,8 +150,8 @@
private final List<CodeReviewCommit> toMerge;
private List<Change> submitted;
private final Map<Change.Id, CodeReviewCommit> commits;
- private ReviewDb schema;
- private Repository db;
+ private ReviewDb db;
+ private Repository repo;
private RevWalk rw;
private RevFlag CAN_MERGE;
private CodeReviewCommit branchTip;
@@ -159,7 +159,7 @@
private Set<RevCommit> alreadyAccepted;
private RefUpdate branchUpdate;
- private final ChangeHookRunner hooks;
+ private final ChangeHooks hooks;
private final AccountCache accountCache;
private final TagCache tagCache;
private final CreateCodeReviewNotes.Factory codeReviewNotesFactory;
@@ -176,7 +176,7 @@
final ChangeControl.GenericFactory changeControlFactory,
@GerritPersonIdent final PersonIdent myIdent,
final MergeQueue mergeQueue, @Assisted final Branch.NameKey branch,
- final ChangeHookRunner hooks, final AccountCache accountCache,
+ final ChangeHooks hooks, final AccountCache accountCache,
final TagCache tagCache, final CreateCodeReviewNotes.Factory crnf,
final SubmoduleOp.Factory subOpFactory) {
repoManager = grm;
@@ -208,7 +208,7 @@
try {
setDestProject();
openRepository();
- final Ref destBranchRef = db.getRef(destBranch.get());
+ final Ref destBranchRef = repo.getRef(destBranch.get());
submitted = new ArrayList<Change>();
submitted.add(change);
@@ -230,7 +230,7 @@
change.setLastSha1MergeTested(new RevId(""));
}
change.setMergeable(isMergeable(change));
- schema.changes().update(Collections.singleton(change));
+ db.changes().update(Collections.singleton(change));
}
} catch (MergeException e) {
log.error("Test merge attempt for change: " + change.getId()
@@ -242,10 +242,10 @@
log.error("Test merge attempt for change: " + change.getId()
+ " failed", e);
} finally {
- if (schema != null) {
- schema.close();
+ if (db != null) {
+ db.close();
}
- schema = null;
+ db = null;
}
}
@@ -258,8 +258,8 @@
}
private void openSchema() throws OrmException {
- if (schema == null) {
- schema = schemaFactory.open();
+ if (db == null) {
+ db = schemaFactory.open();
}
}
@@ -268,7 +268,7 @@
try {
openSchema();
openRepository();
- submitted = schema.changes().submitted(destBranch).toList();
+ submitted = db.changes().submitted(destBranch).toList();
preMerge();
updateBranch();
updateChangeStatus();
@@ -279,11 +279,11 @@
if (rw != null) {
rw.release();
}
- if (db != null) {
- db.close();
+ if (repo != null) {
+ repo.close();
}
- schema.close();
- schema = null;
+ db.close();
+ db = null;
}
}
@@ -310,13 +310,13 @@
private void openRepository() throws MergeException {
final Project.NameKey name = destBranch.getParentKey();
try {
- db = repoManager.openRepository(name);
+ repo = repoManager.openRepository(name);
} catch (RepositoryNotFoundException notGit) {
final String m = "Repository \"" + name.get() + "\" unknown.";
throw new MergeException(m, notGit);
}
- rw = new RevWalk(db) {
+ rw = new RevWalk(repo) {
@Override
protected RevCommit createCommit(final AnyObjectId id) {
return new CodeReviewCommit(id);
@@ -331,7 +331,7 @@
alreadyAccepted = new HashSet<RevCommit>();
try {
- branchUpdate = db.updateRef(destBranch.get());
+ branchUpdate = repo.updateRef(destBranch.get());
if (branchUpdate.getOldObjectId() != null) {
branchTip =
(CodeReviewCommit) rw.parseCommit(branchUpdate.getOldObjectId());
@@ -340,7 +340,7 @@
branchTip = null;
}
- for (final Ref r : db.getAllRefs().values()) {
+ for (final Ref r : repo.getAllRefs().values()) {
if (r.getName().startsWith(Constants.R_HEADS)
|| r.getName().startsWith(Constants.R_TAGS)) {
try {
@@ -357,7 +357,7 @@
private void validateChangeList() throws MergeException {
final Set<ObjectId> tips = new HashSet<ObjectId>();
- for (final Ref r : db.getAllRefs().values()) {
+ for (final Ref r : repo.getAllRefs().values()) {
tips.add(r.getObjectId());
}
@@ -372,7 +372,7 @@
final PatchSet ps;
try {
- ps = schema.patchSets().get(chg.currentPatchSetId());
+ ps = db.patchSets().get(chg.currentPatchSetId());
} catch (OrmException e) {
throw new MergeException("Cannot query the database", e);
}
@@ -501,11 +501,11 @@
// Settings for this project allow us to try and
// automatically resolve conflicts within files if needed.
// Use ResolveMerge and instruct to operate in core.
- m = MergeStrategy.RESOLVE.newMerger(db, true);
+ m = MergeStrategy.RESOLVE.newMerger(repo, true);
} else {
// No auto conflict resolving allowed. If any of the
// affected files was modified, merge will fail.
- m = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ m = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(repo);
}
try {
@@ -688,11 +688,11 @@
// Settings for this project allow us to try and
// automatically resolve conflicts within files if needed.
// Use ResolveMerge and instruct to operate in core.
- m = MergeStrategy.RESOLVE.newMerger(db, true);
+ m = MergeStrategy.RESOLVE.newMerger(repo, true);
} else {
// No auto conflict resolving allowed. If any of the
// affected files was modified, merge will fail.
- m = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ m = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(repo);
}
try {
@@ -807,7 +807,7 @@
List<PatchSetApproval> approvalList = null;
try {
approvalList =
- schema.patchSetApprovals().byPatchSet(n.patchsetId).toList();
+ db.patchSetApprovals().byPatchSet(n.patchsetId).toList();
Collections.sort(approvalList, new Comparator<PatchSetApproval>() {
public int compare(final PatchSetApproval a, final PatchSetApproval b) {
return a.getGranted().compareTo(b.getGranted());
@@ -893,7 +893,7 @@
final CodeReviewCommit newCommit = (CodeReviewCommit) rw.parseCommit(id);
n.change =
- schema.changes().atomicUpdate(n.change.getId(),
+ db.changes().atomicUpdate(n.change.getId(),
new AtomicUpdate<Change>() {
@Override
public Change update(Change change) {
@@ -907,10 +907,10 @@
ps.setUploader(submitAudit.getAccountId());
ps.setRevision(new RevId(id.getName()));
insertAncestors(ps.getId(), newCommit);
- schema.patchSets().insert(Collections.singleton(ps));
+ db.patchSets().insert(Collections.singleton(ps));
n.change =
- schema.changes().atomicUpdate(n.change.getId(),
+ db.changes().atomicUpdate(n.change.getId(),
new AtomicUpdate<Change>() {
@Override
public Change update(Change change) {
@@ -922,7 +922,7 @@
if (approvalList != null) {
for (PatchSetApproval a : approvalList) {
- schema.patchSetApprovals().insert(
+ db.patchSetApprovals().insert(
Collections.singleton(new PatchSetApproval(ps.getId(), a)));
}
}
@@ -945,7 +945,7 @@
a.setAncestorRevision(new RevId(src.getParent(p).getId().name()));
toInsert.add(a);
}
- schema.patchSetAncestors().insert(toInsert);
+ db.patchSetAncestors().insert(toInsert);
}
private ObjectId commit(final Merger m, final CommitBuilder mergeCommit)
@@ -992,7 +992,7 @@
if (GitRepositoryManager.REF_CONFIG.equals(branchUpdate.getName())) {
try {
ProjectConfig cfg = new ProjectConfig(destProject.getNameKey());
- cfg.load(db, mergeTip);
+ cfg.load(repo, mergeTip);
} catch (Exception e) {
throw new MergeException("Submit would store invalid"
+ " project configuration " + mergeTip.name() + " for "
@@ -1078,7 +1078,7 @@
}
case CLEAN_PICK: {
- setMerged(c, message(c, txt));
+ setMerged(c, message(c, txt + " as " + commit.name()));
merged.add(commit);
break;
}
@@ -1111,7 +1111,7 @@
}
CreateCodeReviewNotes codeReviewNotes =
- codeReviewNotesFactory.create(schema, db);
+ codeReviewNotesFactory.create(db, repo);
try {
codeReviewNotes.create(merged, computeAuthor(merged));
} catch (CodeReviewNoteCreationException e) {
@@ -1124,7 +1124,7 @@
private void updateSubscriptions() throws MergeException {
if (mergeTip != null && (branchTip == null || branchTip != mergeTip)) {
SubmoduleOp subOp =
- subOpFactory.create(destBranch, mergeTip, rw, db, destProject,
+ subOpFactory.create(destBranch, mergeTip, rw, repo, destProject,
submitted, commits);
try {
subOp.update();
@@ -1236,11 +1236,11 @@
if (commit.patchsetId == null) {
try {
List<PatchSet> matches =
- schema.patchSets().byRevision(new RevId(commit.name())).toList();
+ db.patchSets().byRevision(new RevId(commit.name())).toList();
if (matches.size() == 1) {
final PatchSet ps = matches.get(0);
commit.patchsetId = ps.getId();
- commit.change = schema.changes().get(ps.getId().getParentKey());
+ commit.change = db.changes().get(ps.getId().getParentKey());
}
} catch (OrmException e) {
}
@@ -1250,7 +1250,7 @@
private boolean isAlreadySent(final Change c, final String prefix) {
try {
final List<ChangeMessage> msgList =
- schema.changeMessages().byChange(c.getId()).toList();
+ db.changeMessages().byChange(c.getId()).toList();
if (msgList.size() > 0) {
final ChangeMessage last = msgList.get(msgList.size() - 1);
if (last.getAuthor() == null && last.getMessage().startsWith(prefix)) {
@@ -1274,7 +1274,7 @@
private ChangeMessage message(final Change c, final String body) {
final String uuid;
try {
- uuid = ChangeUtil.messageUUID(schema);
+ uuid = ChangeUtil.messageUUID(db);
} catch (OrmException e) {
return null;
}
@@ -1292,7 +1292,7 @@
PatchSetApproval submitter = null;
try {
final List<PatchSetApproval> approvals =
- schema.patchSetApprovals().byPatchSet(c).toList();
+ db.patchSetApprovals().byPatchSet(c).toList();
for (PatchSetApproval a : approvals) {
if (a.getValue() > 0
&& ApprovalCategory.SUBMIT.equals(a.getCategoryId())) {
@@ -1315,7 +1315,7 @@
final PatchSet.Id merged = commit.change.currentPatchSetId();
try {
- schema.changes().atomicUpdate(changeId, new AtomicUpdate<Change>() {
+ db.changes().atomicUpdate(changeId, new AtomicUpdate<Change>() {
@Override
public Change update(Change c) {
c.setStatus(Change.Status.MERGED);
@@ -1328,7 +1328,7 @@
// Go back to the patch set that was actually merged.
//
try {
- c.setCurrentPatchSet(patchSetInfoFactory.get(schema, merged));
+ c.setCurrentPatchSet(patchSetInfoFactory.get(db, merged));
} catch (PatchSetInfoNotAvailableException e1) {
log.error("Cannot read merged patch set " + merged, e1);
}
@@ -1352,7 +1352,7 @@
try {
c.setStatus(Change.Status.MERGED);
final List<PatchSetApproval> approvals =
- schema.patchSetApprovals().byChange(changeId).toList();
+ db.patchSetApprovals().byChange(changeId).toList();
final FunctionState fs = functionState.create(
changeControlFactory.controlFor(
c,
@@ -1372,7 +1372,7 @@
}
a.cache(c);
}
- schema.patchSetApprovals().update(approvals);
+ db.patchSetApprovals().update(approvals);
} catch (NoSuchChangeException err) {
log.warn("Cannot normalize approvals for change " + changeId, err);
} catch (OrmException err) {
@@ -1384,7 +1384,7 @@
msg.setAuthor(submitter.getAccountId());
}
try {
- schema.changeMessages().insert(Collections.singleton(msg));
+ db.changeMessages().insert(Collections.singleton(msg));
} catch (OrmException err) {
log.warn("Cannot store message on change", err);
}
@@ -1395,7 +1395,7 @@
if (submitter != null) {
cm.setFrom(submitter.getAccountId());
}
- cm.setPatchSet(schema.patchSets().get(c.currentPatchSetId()));
+ cm.setPatchSet(db.patchSets().get(c.currentPatchSetId()));
cm.send();
} catch (OrmException e) {
log.error("Cannot send email for submitted patch set " + c.getId(), e);
@@ -1406,7 +1406,7 @@
try {
hooks.doChangeMergedHook(c, //
accountCache.get(submitter.getAccountId()).getAccount(), //
- schema.patchSets().get(c.currentPatchSetId()), schema);
+ db.patchSets().get(c.currentPatchSetId()), db);
} catch (OrmException ex) {
log.error("Cannot run hook for submitted patch set " + c.getId(), ex);
}
@@ -1418,14 +1418,14 @@
private void sendMergeFail(Change c, ChangeMessage msg, final boolean makeNew) {
try {
- schema.changeMessages().insert(Collections.singleton(msg));
+ db.changeMessages().insert(Collections.singleton(msg));
} catch (OrmException err) {
log.warn("Cannot record merge failure message", err);
}
if (makeNew) {
try {
- schema.changes().atomicUpdate(c.getId(), new AtomicUpdate<Change>() {
+ db.changes().atomicUpdate(c.getId(), new AtomicUpdate<Change>() {
@Override
public Change update(Change c) {
if (c.getStatus().isOpen()) {
@@ -1441,7 +1441,7 @@
}
} else {
try {
- ChangeUtil.touch(c, schema);
+ ChangeUtil.touch(c, db);
} catch (OrmException err) {
log.warn("Cannot update change timestamp", err);
}
@@ -1453,7 +1453,7 @@
if (submitter != null) {
cm.setFrom(submitter.getAccountId());
}
- cm.setPatchSet(schema.patchSets().get(c.currentPatchSetId()));
+ cm.setPatchSet(db.patchSets().get(c.currentPatchSetId()));
cm.setChangeMessage(msg);
cm.send();
} catch (OrmException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 0d5bbb4..8450dc5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.git;
-import com.google.gerrit.common.ChangeHookRunner;
+import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.ApprovalType;
import com.google.gerrit.common.data.ApprovalTypes;
@@ -33,6 +33,7 @@
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.RevId;
import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
@@ -124,7 +125,7 @@
private final ReplacePatchSetSender.Factory replacePatchSetFactory;
private final ReplicationQueue replication;
private final PatchSetInfoFactory patchSetInfoFactory;
- private final ChangeHookRunner hooks;
+ private final ChangeHooks hooks;
private final GitRepositoryManager repoManager;
private final ProjectCache projectCache;
private final String canonicalWebUrl;
@@ -163,7 +164,7 @@
final ReplacePatchSetSender.Factory replacePatchSetFactory,
final ReplicationQueue replication,
final PatchSetInfoFactory patchSetInfoFactory,
- final ChangeHookRunner hooks,
+ final ChangeHooks hooks,
final ProjectCache projectCache,
final GitRepositoryManager repoManager,
final TagCache tagCache,
@@ -1830,12 +1831,7 @@
change.setStatus(Change.Status.MERGED);
ChangeUtil.updated(change);
- final List<PatchSetApproval> approvals =
- db.patchSetApprovals().byChange(change.getId()).toList();
- for (PatchSetApproval a : approvals) {
- a.cache(change);
- }
- db.patchSetApprovals().update(approvals);
+ ApprovalsUtil.syncChangeStatus(db, change);
final StringBuilder msgBuf = new StringBuilder();
msgBuf.append("Change has been successfully pushed");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
index 0c81252..822bd51 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
@@ -417,8 +417,12 @@
authors.add(patchSet.getUploader());
}
if (patchSetInfo != null) {
- authors.add(patchSetInfo.getAuthor().getAccount());
- authors.add(patchSetInfo.getCommitter().getAccount());
+ if (patchSetInfo.getAuthor().getAccount() != null) {
+ authors.add(patchSetInfo.getAuthor().getAccount());
+ }
+ if (patchSetInfo.getCommitter().getAccount() != null) {
+ authors.add(patchSetInfo.getCommitter().getAccount());
+ }
}
return authors;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailTokenVerifier.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailTokenVerifier.java
new file mode 100644
index 0000000..374ae34
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailTokenVerifier.java
@@ -0,0 +1,67 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package com.google.gerrit.server.mail;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.server.account.AuthRequest;
+
+/** Verifies the token sent by {@link RegisterNewEmailSender}. */
+public interface EmailTokenVerifier {
+ /**
+ * Construct a token to verify an email address for a user.
+ *
+ * @param accountId the caller that wants to add an email to their account.
+ * @param emailAddress the address to add.
+ * @return an unforgeable string to email to {@code emailAddress}. Presenting
+ * the string provides proof the user has the ability to read messages
+ * sent to that address.
+ */
+ public String encode(Account.Id accountId, String emailAddress);
+
+ /**
+ * Decode a token previously created.
+ * @param tokenString the string created by encode.
+ * @return a pair of account id and email address.
+ * @throws InvalidTokenException the token is invalid, expired, malformed, etc.
+ */
+ public ParsedToken decode(String tokenString) throws InvalidTokenException;
+
+ /** Exception thrown when a token does not parse correctly. */
+ public static class InvalidTokenException extends Exception {
+ public InvalidTokenException() {
+ super("Invalid token");
+ }
+
+ public InvalidTokenException(Throwable cause) {
+ super("Invalid token", cause);
+ }
+ }
+
+ /** Pair returned from decode to provide the data used during encode. */
+ public static class ParsedToken {
+ private final Account.Id accountId;
+ private final String emailAddress;
+
+ public ParsedToken(Account.Id accountId, String emailAddress) {
+ this.accountId = accountId;
+ this.emailAddress = emailAddress;
+ }
+
+ public Account.Id getAccountId() {
+ return accountId;
+ }
+
+ public String getEmailAddress() {
+ return emailAddress;
+ }
+
+ public AuthRequest toAuthRequest() {
+ return AuthRequest.forEmail(getEmailAddress());
+ }
+
+ @Override
+ public String toString() {
+ return accountId + " adds " + emailAddress;
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/RegisterNewEmailSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RegisterNewEmailSender.java
index bc967da..17fe9c6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/RegisterNewEmailSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RegisterNewEmailSender.java
@@ -14,30 +14,30 @@
package com.google.gerrit.server.mail;
+import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.AnonymousCowardName;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.gwtjsonrpc.server.XsrfException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
-import org.eclipse.jgit.util.Base64;
-
-import java.io.UnsupportedEncodingException;
-
public class RegisterNewEmailSender extends OutgoingEmail {
public interface Factory {
public RegisterNewEmailSender create(String address);
}
- private final AuthConfig authConfig;
+ private final EmailTokenVerifier tokenVerifier;
+ private final IdentifiedUser user;
private final String addr;
+ private String emailToken;
@Inject
- public RegisterNewEmailSender(EmailArguments ea, AuthConfig ac,
+ public RegisterNewEmailSender(EmailArguments ea,
+ EmailTokenVerifier etv,
@AnonymousCowardName String anonymousCowardName,
+ IdentifiedUser callingUser,
@Assisted final String address) {
super(ea, anonymousCowardName, "registernewemail");
- authConfig = ac;
+ tokenVerifier = etv;
+ user = callingUser;
addr = address;
}
@@ -58,14 +58,29 @@
appendText(velocifyFile("RegisterNewEmail.vm"));
}
- public String getEmailRegistrationToken() {
- try {
- return authConfig.getEmailRegistrationToken().newToken(
- Base64.encodeBytes(addr.getBytes("UTF-8")));
- } catch (XsrfException e) {
- throw new IllegalArgumentException(e);
- } catch (UnsupportedEncodingException e) {
- throw new IllegalArgumentException(e);
+ public String getUserNameEmail() {
+ String name = user.getAccount().getFullName();
+ String email = user.getAccount().getPreferredEmail();
+
+ if (name != null && email != null) {
+ return name + " <" + email + ">";
+ } else if (email != null) {
+ return email;
+ } else if (name != null) {
+ return name;
+ } else {
+ String username = user.getUserName();
+ if (username != null) {
+ return username;
+ }
}
+ return null;
+ }
+
+ public String getEmailRegistrationToken() {
+ if (emailToken == null) {
+ emailToken = tokenVerifier.encode(user.getAccountId(), addr);
+ }
+ return emailToken;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java
new file mode 100644
index 0000000..aad2f76
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java
@@ -0,0 +1,81 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package com.google.gerrit.server.mail;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.gwtjsonrpc.server.SignedToken;
+import com.google.gwtjsonrpc.server.ValidToken;
+import com.google.gwtjsonrpc.server.XsrfException;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+
+import org.eclipse.jgit.util.Base64;
+
+import java.io.UnsupportedEncodingException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Verifies the token sent by {@link RegisterNewEmailSender}. */
+public class SignedTokenEmailTokenVerifier implements EmailTokenVerifier {
+ private final SignedToken emailRegistrationToken;
+
+ public static class Module extends AbstractModule {
+ @Override
+ protected void configure() {
+ bind(EmailTokenVerifier.class).to(SignedTokenEmailTokenVerifier.class);
+ }
+ }
+
+ @Inject
+ SignedTokenEmailTokenVerifier(AuthConfig config) {
+ emailRegistrationToken = config.getEmailRegistrationToken();
+ }
+
+ public String encode(Account.Id accountId, String emailAddress) {
+ try {
+ String payload = String.format("%s:%s", accountId, emailAddress);
+ byte[] utf8 = payload.getBytes("UTF-8");
+ String base64 = Base64.encodeBytes(utf8);
+ return emailRegistrationToken.newToken(base64);
+ } catch (XsrfException e) {
+ throw new IllegalArgumentException(e);
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public ParsedToken decode(String tokenString) throws InvalidTokenException {
+ ValidToken token;
+ try {
+ token = emailRegistrationToken.checkToken(tokenString, null);
+ } catch (XsrfException err) {
+ throw new InvalidTokenException(err);
+ }
+ if (token == null || token.getData() == null || token.getData().isEmpty()) {
+ throw new InvalidTokenException();
+ }
+
+ String payload;
+ try {
+ payload = new String(Base64.decode(token.getData()), "UTF-8");
+ } catch (UnsupportedEncodingException err) {
+ throw new InvalidTokenException(err);
+ }
+
+ Matcher matcher = Pattern.compile("^([0-9]+):(.+@.+)$").matcher(payload);
+ if (!matcher.matches()) {
+ throw new InvalidTokenException();
+ }
+
+ Account.Id id;
+ try {
+ id = Account.Id.parse(matcher.group(1));
+ } catch (IllegalArgumentException err) {
+ throw new InvalidTokenException(err);
+ }
+
+ String newEmail = matcher.group(2);
+ return new ParsedToken(id, newEmail);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PublishComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PublishComments.java
index a276c84..039e0f6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PublishComments.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PublishComments.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.patch;
-import com.google.gerrit.common.ChangeHookRunner;
+import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.common.data.ApprovalType;
import com.google.gerrit.common.data.ApprovalTypes;
import com.google.gerrit.reviewdb.ApprovalCategory;
@@ -67,7 +67,7 @@
private final PatchSetInfoFactory patchSetInfoFactory;
private final ChangeControl.Factory changeControlFactory;
private final FunctionState.Factory functionStateFactory;
- private final ChangeHookRunner hooks;
+ private final ChangeHooks hooks;
private final PatchSet.Id patchSetId;
private final String messageText;
@@ -86,7 +86,7 @@
final PatchSetInfoFactory patchSetInfoFactory,
final ChangeControl.Factory changeControlFactory,
final FunctionState.Factory functionStateFactory,
- final ChangeHookRunner hooks,
+ final ChangeHooks hooks,
@Assisted final PatchSet.Id patchSetId,
@Assisted final String messageText,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index e229ac2..073372c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -189,6 +189,16 @@
;
}
+ /** Can this user publish this draft change or any draft patch set of this change? */
+ public boolean canPublish(final ReviewDb db) throws OrmException {
+ return isOwner() && isVisible(db);
+ }
+
+ /** Can this user delete this draft change or any draft patch set of this change? */
+ public boolean canDeleteDraft(final ReviewDb db) throws OrmException {
+ return isOwner() && isVisible(db);
+ }
+
/** Can this user restore this change? */
public boolean canRestore() {
return canAbandon(); // Anyone who can abandon the change can restore it back
@@ -526,4 +536,4 @@
}
return list;
}
-}
\ No newline at end of file
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheClock.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheClock.java
new file mode 100644
index 0000000..f86562c
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheClock.java
@@ -0,0 +1,78 @@
+// 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.project;
+
+import com.google.gerrit.server.config.ConfigUtil;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import org.eclipse.jgit.lib.Config;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** Ticks periodically to force refresh events for {@link ProjectCacheImpl}. */
+@Singleton
+public class ProjectCacheClock {
+ private volatile long generation;
+
+ @Inject
+ public ProjectCacheClock(@GerritServerConfig Config serverConfig) {
+ this(TimeUnit.MILLISECONDS.convert(
+ ConfigUtil.getTimeUnit(serverConfig,
+ "cache", "projects", "checkFrequency",
+ 5, TimeUnit.MINUTES), TimeUnit.MINUTES));
+ }
+
+ public ProjectCacheClock(long checkFrequencyMillis) {
+ if (10 < checkFrequencyMillis) {
+ // Start with generation 1 (to avoid magic 0 below).
+ generation = 1;
+ ThreadFactory factory = new ThreadFactory() {
+ private final AtomicInteger id = new AtomicInteger();
+
+ @Override
+ public Thread newThread(Runnable runnable) {
+ Thread thread = Executors.defaultThreadFactory().newThread(runnable);
+ thread.setName(String.format("ProjectCacheClock-%d", id.incrementAndGet()));
+ thread.setDaemon(true);
+ thread.setPriority(Thread.MIN_PRIORITY);
+ return thread;
+ }
+ };
+ ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, factory);
+ executor.scheduleAtFixedRate(new Runnable() {
+ @Override
+ public void run() {
+ // This is not exactly thread-safe, but is OK for our use.
+ // The only thread that writes the volatile is this task.
+ generation = generation + 1;
+ }
+ }, checkFrequencyMillis, checkFrequencyMillis, TimeUnit.MILLISECONDS);
+ } else {
+ // Magic generation 0 triggers ProjectState to always
+ // check on each needsRefresh() request we make to it.
+ generation = 0;
+ }
+ }
+
+ long read() {
+ return generation;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
index ff68720..7a7f56e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
@@ -19,8 +19,6 @@
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.EntryCreator;
import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.inject.Inject;
@@ -30,7 +28,6 @@
import com.google.inject.name.Named;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Repository;
import java.util.Collections;
@@ -38,8 +35,6 @@
import java.util.NoSuchElementException;
import java.util.SortedSet;
import java.util.TreeSet;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -71,39 +66,19 @@
private final Cache<Project.NameKey, ProjectState> byName;
private final Cache<ListKey,SortedSet<Project.NameKey>> list;
private final Lock listLock;
- private volatile long generation;
+ private final ProjectCacheClock clock;
@Inject
ProjectCacheImpl(
final AllProjectsName allProjectsName,
@Named(CACHE_NAME) final Cache<Project.NameKey, ProjectState> byName,
@Named(CACHE_LIST) final Cache<ListKey, SortedSet<Project.NameKey>> list,
- @GerritServerConfig final Config serverConfig) {
+ ProjectCacheClock clock) {
this.allProjectsName = allProjectsName;
this.byName = byName;
this.list = list;
this.listLock = new ReentrantLock(true /* fair */);
-
- long checkFrequencyMillis = TimeUnit.MILLISECONDS.convert(
- ConfigUtil.getTimeUnit(serverConfig,
- "cache", "projects", "checkFrequency",
- 5, TimeUnit.MINUTES), TimeUnit.MINUTES);
- if (10 < checkFrequencyMillis) {
- // Start with generation 1 (to avoid magic 0 below).
- generation = 1;
- Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
- @Override
- public void run() {
- // This is not exactly thread-safe, but is OK for our use.
- // The only thread that writes the volatile is this task.
- generation = generation + 1;
- }
- }, checkFrequencyMillis, checkFrequencyMillis, TimeUnit.MILLISECONDS);
- } else {
- // Magic generation 0 triggers ProjectState to always
- // check on each needsRefresh() request we make to it.
- generation = 0;
- }
+ this.clock = clock;
}
@Override
@@ -125,7 +100,7 @@
*/
public ProjectState get(final Project.NameKey projectName) {
ProjectState state = byName.get(projectName);
- if (state != null && state.needsRefresh(generation)) {
+ if (state != null && state.needsRefresh(clock.read())) {
byName.remove(projectName);
state = byName.get(projectName);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SuggestParentCandidates.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SuggestParentCandidates.java
new file mode 100644
index 0000000..9585e1e
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SuggestParentCandidates.java
@@ -0,0 +1,81 @@
+// 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.server.project;
+
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+public class SuggestParentCandidates {
+ public interface Factory {
+ SuggestParentCandidates create();
+ }
+
+ private final ProjectControl.Factory projectControlFactory;
+ private final ProjectCache projectCache;
+ private final AllProjectsName allProject;
+
+ @Inject
+ SuggestParentCandidates(final ProjectControl.Factory projectControlFactory,
+ final ProjectCache projectCache, final AllProjectsName allProject) {
+ this.projectControlFactory = projectControlFactory;
+ this.projectCache = projectCache;
+ this.allProject = allProject;
+ }
+
+ public List<Project.NameKey> getNameKeys() throws OrmException,
+ NoSuchProjectException {
+ List<Project> pList = getProjects();
+ final List<Project.NameKey> nameKeys =
+ new ArrayList<Project.NameKey>(pList.size());
+ for (Project p : pList) {
+ nameKeys.add(p.getNameKey());
+ }
+ return nameKeys;
+ }
+
+ public List<Project> getProjects() throws OrmException,
+ NoSuchProjectException {
+ Set<Project> projects = new TreeSet<Project>(new Comparator<Project>() {
+ @Override
+ public int compare(Project o1, Project o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
+ for (Project.NameKey p : projectCache.all()) {
+ try {
+ final ProjectControl control = projectControlFactory.controlFor(p);
+ final Project.NameKey parentK = control.getProject().getParent();
+ if (parentK != null) {
+ ProjectControl pControl = projectControlFactory.controlFor(parentK);
+ if (pControl.isVisible() || pControl.isOwner()) {
+ projects.add(pControl.getProject());
+ }
+ }
+ } catch (NoSuchProjectException e) {
+ continue;
+ }
+ }
+ projects.add(projectControlFactory.controlFor(allProject).getProject());
+ return new ArrayList<Project>(projects);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index cbb15f7..64a5d07 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -397,7 +397,7 @@
if (g == null) {
throw error("Group " + group + " not found");
}
- return new OwnerinPredicate(args.dbProvider, args.userFactory, g.getId());
+ return new OwnerinPredicate(args.dbProvider, args.userFactory, g.getGroupUUID());
}
@Operator
@@ -425,7 +425,7 @@
if (g == null) {
throw error("Group " + group + " not found");
}
- return new ReviewerinPredicate(args.dbProvider, args.userFactory, g.getId());
+ return new ReviewerinPredicate(args.dbProvider, args.userFactory, g.getGroupUUID());
}
@Operator
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OwnerinPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
index 4f6fe5a5..92890b7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
@@ -25,18 +25,18 @@
class OwnerinPredicate extends OperatorPredicate<ChangeData> {
private final Provider<ReviewDb> dbProvider;
private final IdentifiedUser.GenericFactory userFactory;
- private final AccountGroup.Id id;
+ private final AccountGroup.UUID uuid;
OwnerinPredicate(Provider<ReviewDb> dbProvider,
- IdentifiedUser.GenericFactory userFactory, AccountGroup.Id id) {
- super(ChangeQueryBuilder.FIELD_OWNERIN, id.toString());
+ IdentifiedUser.GenericFactory userFactory, AccountGroup.UUID uuid) {
+ super(ChangeQueryBuilder.FIELD_OWNERIN, uuid.toString());
this.dbProvider = dbProvider;
this.userFactory = userFactory;
- this.id = id;
+ this.uuid = uuid;
}
- AccountGroup.Id getAccountGroupId() {
- return id;
+ AccountGroup.UUID getAccountGroupUUID() {
+ return uuid;
}
@Override
@@ -47,7 +47,7 @@
}
final IdentifiedUser owner = userFactory.create(dbProvider,
change.getOwner());
- return owner.getEffectiveGroups().contains(id);
+ return owner.getEffectiveGroups().contains(uuid);
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
index 2ee9b3f..15ca203 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
@@ -100,10 +100,18 @@
includePatchSets = on;
}
+ public boolean getIncludePatchSets() {
+ return includePatchSets;
+ }
+
public void setIncludeCurrentPatchSet(boolean on) {
includeCurrentPatchSet = on;
}
+ public boolean getIncludeCurrentPatchSet() {
+ return includeCurrentPatchSet;
+ }
+
public void setIncludeApprovals(boolean on) {
includeApprovals = on;
}
@@ -116,6 +124,10 @@
includeFiles = on;
}
+ public boolean getIncludeFiles() {
+ return includeFiles;
+ }
+
public void setIncludeCommitMessage(boolean on) {
includeCommitMessage = on;
}
@@ -125,6 +137,51 @@
this.outputFormat = fmt;
}
+ public List<ChangeData> queryChanges(final String queryString)
+ throws OrmException, QueryParseException {
+ final Predicate<ChangeData> visibleToMe = queryBuilder.is_visible();
+ Predicate<ChangeData> s = compileQuery(queryString, visibleToMe);
+ List<ChangeData> results = new ArrayList<ChangeData>();
+ HashSet<Change.Id> want = new HashSet<Change.Id>();
+ for (ChangeData d : ((ChangeDataSource) s).read()) {
+ if (d.hasChange()) {
+ // Checking visibleToMe here should be unnecessary, the
+ // query should have already performed it. But we don't
+ // want to trust the query rewriter that much yet.
+ //
+ if (visibleToMe.match(d)) {
+ results.add(d);
+ }
+ } else {
+ want.add(d.getId());
+ }
+ }
+
+ if (!want.isEmpty()) {
+ for (Change c : db.get().changes().get(want)) {
+ ChangeData d = new ChangeData(c);
+ if (visibleToMe.match(d)) {
+ results.add(d);
+ }
+ }
+ }
+
+ Collections.sort(results, new Comparator<ChangeData>() {
+ @Override
+ public int compare(ChangeData a, ChangeData b) {
+ return b.getChange().getSortKey().compareTo(
+ a.getChange().getSortKey());
+ }
+ });
+
+ int limit = limit(s);
+ if (limit < results.size()) {
+ results = results.subList(0, limit);
+ }
+
+ return results;
+ }
+
public void query(String queryString) throws IOException {
out = new PrintWriter( //
new BufferedWriter( //
@@ -141,46 +198,7 @@
final QueryStats stats = new QueryStats();
stats.runTimeMilliseconds = System.currentTimeMillis();
- final Predicate<ChangeData> visibleToMe = queryBuilder.is_visible();
- Predicate<ChangeData> s = compileQuery(queryString, visibleToMe);
- List<ChangeData> results = new ArrayList<ChangeData>();
- HashSet<Change.Id> want = new HashSet<Change.Id>();
- for (ChangeData d : ((ChangeDataSource) s).read()) {
- if (d.hasChange()) {
- // Checking visibleToMe here should be unnecessary, the
- // query should have already performed it. But we don't
- // want to trust the query rewriter that much yet.
- //
- if (visibleToMe.match(d)) {
- results.add(d);
- }
- } else {
- want.add(d.getId());
- }
- }
-
- if (!want.isEmpty()) {
- for (Change c : db.get().changes().get(want)) {
- ChangeData d = new ChangeData(c);
- if (visibleToMe.match(d)) {
- results.add(d);
- }
- }
- }
-
- Collections.sort(results, new Comparator<ChangeData>() {
- @Override
- public int compare(ChangeData a, ChangeData b) {
- return b.getChange().getSortKey().compareTo(
- a.getChange().getSortKey());
- }
- });
-
- int limit = limit(s);
- if (limit < results.size()) {
- results = results.subList(0, limit);
- }
-
+ List<ChangeData> results = queryChanges(queryString);
for (ChangeData d : results) {
ChangeAttribute c = eventFactory.asChangeAttribute(d.getChange());
eventFactory.extend(c, d.getChange());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
index 1784d9a..ea4d7b6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
@@ -25,18 +25,18 @@
class ReviewerinPredicate extends OperatorPredicate<ChangeData> {
private final Provider<ReviewDb> dbProvider;
private final IdentifiedUser.GenericFactory userFactory;
- private final AccountGroup.Id id;
+ private final AccountGroup.UUID uuid;
ReviewerinPredicate(Provider<ReviewDb> dbProvider,
- IdentifiedUser.GenericFactory userFactory, AccountGroup.Id id) {
- super(ChangeQueryBuilder.FIELD_REVIEWERIN, id.toString());
+ IdentifiedUser.GenericFactory userFactory, AccountGroup.UUID uuid) {
+ super(ChangeQueryBuilder.FIELD_REVIEWERIN, uuid.toString());
this.dbProvider = dbProvider;
this.userFactory = userFactory;
- this.id = id;
+ this.uuid = uuid;
}
- AccountGroup.Id getAccountGroupId() {
- return id;
+ AccountGroup.UUID getAccountGroupUUID() {
+ return uuid;
}
@Override
@@ -44,7 +44,7 @@
for (PatchSetApproval p : object.approvals(dbProvider)) {
final IdentifiedUser reviewer = userFactory.create(dbProvider,
p.getAccountId());
- if (reviewer.getEffectiveGroups().contains(id)) {
+ if (reviewer.getEffectiveGroups().contains(uuid)) {
return true;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
index abdc0ee..25db3c6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -32,7 +32,7 @@
/** A version of the database schema. */
public abstract class SchemaVersion {
/** The current schema version. */
- public static final Class<Schema_61> C = Schema_61.class;
+ public static final Class<Schema_62> C = Schema_62.class;
public static class Module extends AbstractModule {
@Override
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestAccountsEnum.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_62.java
similarity index 72%
rename from gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestAccountsEnum.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_62.java
index 2ef2d44..4de8e55 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestAccountsEnum.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_62.java
@@ -12,11 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.httpd.rpc;
+package com.google.gerrit.server.schema;
-public enum SuggestAccountsEnum {
- ALL,
- SAME_GROUP,
- VISIBLE_GROUP,
- OFF;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+public class Schema_62 extends SchemaVersion {
+ @Inject
+ Schema_62(Provider<Schema_61> prior) {
+ super(prior);
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/MagicBranch.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/MagicBranch.java
index c60e1f3..ef08d78 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/util/MagicBranch.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/MagicBranch.java
@@ -30,7 +30,7 @@
LoggerFactory.getLogger(MagicBranch.class);
public static final String NEW_CHANGE = "refs/for/";
- public static final String NEW_DRAFT_CHANGE = "refs/draft/";
+ public static final String NEW_DRAFT_CHANGE = "refs/drafts/";
public static final String NEW_PUBLISH_CHANGE = "refs/publish/";
/** Extracts the destination from a ref name */
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/SubmoduleSectionParser.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/SubmoduleSectionParser.java
index 7388421..3148aaa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/util/SubmoduleSectionParser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/SubmoduleSectionParser.java
@@ -103,6 +103,10 @@
fromIndex = urlExtractedPath.lastIndexOf('/', fromIndex - 1);
projectName = urlExtractedPath.substring(fromIndex + 1);
+ if (projectName.endsWith(".git")) {
+ projectName = projectName.substring(0, projectName.length() - 4);
+ }
+
if (repoManager.list().contains(new Project.NameKey(projectName))) {
return new SubmoduleSubscription(
superProjectBranch,
diff --git a/gerrit-server/src/main/prolog/gerrit_common.pl b/gerrit-server/src/main/prolog/gerrit_common.pl
index 634f8ea..3313162 100644
--- a/gerrit-server/src/main/prolog/gerrit_common.pl
+++ b/gerrit-server/src/main/prolog/gerrit_common.pl
@@ -198,8 +198,8 @@
%%
legacy_submit_rule('MaxWithBlock', Label, Id, Min, Max, T) :- !, max_with_block(Label, Min, Max, T).
legacy_submit_rule('MaxNoBlock', Label, Id, Min, Max, T) :- !, max_no_block(Label, Max, T).
-legacy_submit_rule('NoBlock', Label, Id, Min, Max, T) :- T = ok(_), true.
-legacy_submit_rule('NoOp', Label, Id, Min, Max, T) :- T = ok(_), true.
+legacy_submit_rule('NoBlock', Label, Id, Min, Max, T) :- !, T = ok(_).
+legacy_submit_rule('NoOp', Label, Id, Min, Max, T) :- !, T = ok(_).
legacy_submit_rule(Fun, Label, Id, Min, Max, T) :- T = impossible(unsupported(Fun)).
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RegisterNewEmail.vm b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RegisterNewEmail.vm
index 0e42e01..7e095fb 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RegisterNewEmail.vm
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RegisterNewEmail.vm
@@ -34,12 +34,12 @@
Welcome to Gerrit Code Review at ${email.gerritHost}.
To add a verified email address to your user account, please
-click on the following link:
+click on the following link#if($email.userNameEmail) while signed in as $email.userNameEmail#end:
$email.gerritUrl#/VE/$email.emailRegistrationToken
If you have received this mail in error, you do not need to take any
-action to cancel the account. The account will not be activated, and
+action to cancel the account. The address will not be activated, and
you will not receive any further emails.
If clicking the link above does not work, copy and paste the URL in a
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/tools/root/hooks/commit-msg b/gerrit-server/src/main/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
index a0e3554..548f373 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
@@ -36,56 +36,110 @@
return
fi
+ # Does Change-Id: already exist? if so, exit (no change).
if grep -i '^Change-Id:' "$MSG" >/dev/null
then
return
fi
id=`_gen_ChangeId`
- perl -e '
- $MSG = shift;
- $id = shift;
- $CHANGE_ID_AFTER = shift;
+ T="$MSG.tmp.$$"
+ AWK=awk
+ if [ -x /usr/xpg4/bin/awk ]; then
+ # Solaris AWK is just too broken
+ AWK=/usr/xpg4/bin/awk
+ fi
+ $AWK '
+ # Skip lines starting with "#" without any spaces before it.
+ /^#/ { next }
- undef $/;
- open(I, $MSG); $_ = <I>; close I;
- s|^diff --git a/.*||ms;
- s|^#.*$||mg;
- exit unless $_;
+ # Skip the line starting with the diff command and everything after it,
+ # up to the end of the file, assuming it is only patch data.
+ # If more than one line before the diff was empty, strip all but one.
+ /^diff --git a/ {
+ if (blankLines > 1) {
+ blankLines = 1
+ }
+ while (getline) { }
+ next
+ }
- @message = split /\n/;
- $haveFooter = 0;
- $startFooter = @message;
- for($line = @message - 1; $line >= 0; $line--) {
- $_ = $message[$line];
+ # Handle comments and continuations in tags ([foo: bar] etc)
+ (caught == 1) && /^[ []/ {
+ if (lines != "") {
+ lines = lines "\n"
+ }
+ lines = lines $0
+ next
+ }
- if (/^[a-zA-Z0-9-]+:/ && !m,^[a-z0-9-]+://,) {
- $haveFooter++;
- next;
+ # Handle normal lines (ie. not starting with some tag like "Signed-off-by:").
+ # If normal text appears after tags were "caught", handle them as normal text, too.
+ # Also count blank lines in blankLines.
+ !/^[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\// {
+ if ($0 == "") {
+ blankLines++
+ next
+ } else {
+ for (i = 0; i < blankLines; i++) {
+ print ""
}
- next if /^[ []/;
- $startFooter = $line if ($haveFooter && /^\r?$/);
- last;
+ blankLines = 0
}
-
- @footer = @message[$startFooter+1..@message];
- @message = @message[0..$startFooter];
- push(@footer, "") unless @footer;
-
- for ($line = 0; $line < @footer; $line++) {
- $_ = $footer[$line];
- next if /^($CHANGE_ID_AFTER):/i;
- last;
+ if (caught == 1) {
+ caught = 0
+ print lines
+ lines = ""
}
- splice(@footer, $line, 0, "Change-Id: I$id");
+ print $0
+ next
+ }
- $_ = join("\n", @message, @footer);
- open(O, ">$MSG"); print O; close O;
- ' "$MSG" "$id" "$CHANGE_ID_AFTER"
+ # Handle tags. They are "caught" and collected in the "lines" variable
+ {
+ caught = 1
+ if (lines != "") {
+ lines = lines "\n";
+ }
+ lines = lines $0
+ }
+
+ # Tag handling:
+ # If last line before tags was not blank, there were no tags.
+ # In that case, print everything, plus a blank line, followed by Change-Id.
+ # Otherwise there were tags. Look for the right place to inject Change-Id,
+ # by considering CHANGE_ID_AFTER. Tags listed in it (case insensitive) come first,
+ # then Change-Id, then everything else (eg. Signed-off-by:).
+ END {
+ unprinted = 1
+ if (blankLines == 0) {
+ if (lines == "") {
+ print ""
+ } else {
+ print lines "\n"
+ }
+ } else {
+ for (i = 0; i < blankLines; i++) {
+ print ""
+ }
+ changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):"
+ numlines = split(lines, footer, "\n")
+ for (line = 1; line <= numlines; line++) {
+ if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) {
+ unprinted = 0
+ print "Change-Id: I'"$id"'"
+ }
+ print footer[line]
+ }
+ }
+ if (unprinted) {
+ print "Change-Id: I'"$id"'"
+ }
+ }' "$MSG" > $T && mv $T "$MSG" || rm -f $T
}
_gen_ChangeIdInput() {
echo "tree `git write-tree`"
- if parent=`git rev-parse HEAD^0 2>/dev/null`
+ if parent=`git rev-parse "HEAD^0" 2>/dev/null`
then
echo "parent $parent"
fi
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/util/SubmoduleSectionParserTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/util/SubmoduleSectionParserTest.java
index 55988092..b2c2fc9 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/util/SubmoduleSectionParserTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/util/SubmoduleSectionParserTest.java
@@ -73,12 +73,15 @@
"c-path", "refs/heads/master"));
sectionsToReturn.put("d", new SubmoduleSection("ssh://localhost/d",
"d-parent/the-d-folder", "refs/heads/test"));
+ sectionsToReturn.put("e", new SubmoduleSection("ssh://localhost/e.git", "e",
+ "."));
final Map<String, String> reposToBeFound = new HashMap<String, String>();
reposToBeFound.put("a", "a");
reposToBeFound.put("b", "b");
reposToBeFound.put("c", "test/c");
reposToBeFound.put("d", "d");
+ reposToBeFound.put("e", "e");
final Branch.NameKey superBranchNameKey =
new Branch.NameKey(new Project.NameKey("super-project"),
@@ -98,6 +101,9 @@
expectedSubscriptions.add(new SubmoduleSubscription(superBranchNameKey,
new Branch.NameKey(new Project.NameKey("d"), "refs/heads/test"),
"d-parent/the-d-folder"));
+ expectedSubscriptions
+ .add(new SubmoduleSubscription(superBranchNameKey, new Branch.NameKey(
+ new Project.NameKey("e"), "refs/heads/master"), "e"));
execute(superBranchNameKey, sectionsToReturn, reposToBeFound,
expectedSubscriptions);
@@ -116,12 +122,15 @@
"c-path", "refs/heads/master"));
sectionsToReturn.put("d", new SubmoduleSection("ssh://localhost/d",
"d-parent/the-d-folder", "refs/heads/test"));
+ sectionsToReturn.put("e", new SubmoduleSection("ssh://localhost/e.git", "e",
+ "."));
// "b" will not be in this list
final Map<String, String> reposToBeFound = new HashMap<String, String>();
reposToBeFound.put("a", "a");
reposToBeFound.put("c", "test/c");
reposToBeFound.put("d", "d");
+ reposToBeFound.put("e", "e");
final Branch.NameKey superBranchNameKey =
new Branch.NameKey(new Project.NameKey("super-project"),
@@ -138,6 +147,9 @@
expectedSubscriptions.add(new SubmoduleSubscription(superBranchNameKey,
new Branch.NameKey(new Project.NameKey("d"), "refs/heads/test"),
"d-parent/the-d-folder"));
+ expectedSubscriptions
+ .add(new SubmoduleSubscription(superBranchNameKey, new Branch.NameKey(
+ new Project.NameKey("e"), "refs/heads/master"), "e"));
execute(superBranchNameKey, sectionsToReturn, reposToBeFound,
expectedSubscriptions);
@@ -202,6 +214,9 @@
while (fromIndex > 0) {
fromIndex = urlExtractedPath.lastIndexOf('/', fromIndex - 1);
projectNameCandidate = urlExtractedPath.substring(fromIndex + 1);
+ if (projectNameCandidate.endsWith(".git")) {
+ projectNameCandidate = projectNameCandidate.substring(0, projectNameCandidate.length() - 4);
+ }
if (projectNameCandidate.equals(reposToBeFound.get(id))) {
expect(repoManager.list()).andReturn(
new TreeSet<Project.NameKey>(Collections
diff --git a/gerrit-sshd/pom.xml b/gerrit-sshd/pom.xml
index 0ccf0ec..c02e108 100644
--- a/gerrit-sshd/pom.xml
+++ b/gerrit-sshd/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-sshd</artifactId>
@@ -64,5 +64,11 @@
<artifactId>gerrit-server</artifactId>
<version>${project.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-ehcache</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
</project>
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java
index 89338c4..fb17169 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java
@@ -109,6 +109,7 @@
public void start(final Environment env) throws IOException {
this.env = env;
+ final Context ctx = this.ctx;
startExecutor.execute(new Runnable() {
public void run() {
try {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java
index 313b9dc..083759c 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java
@@ -14,7 +14,7 @@
package com.google.gerrit.sshd.commands;
-import com.google.gerrit.server.cache.CachePool;
+import com.google.gerrit.ehcache.EhcachePoolImpl;
import com.google.gerrit.sshd.BaseCommand;
import com.google.inject.Inject;
@@ -27,7 +27,7 @@
abstract class CacheCommand extends BaseCommand {
@Inject
- protected CachePool cachePool;
+ protected EhcachePoolImpl cachePool;
protected SortedSet<String> cacheNames() {
final SortedSet<String> names = new TreeSet<String>();
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
index a88dfb4..f0327c5 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
@@ -16,11 +16,13 @@
import com.google.gerrit.common.errors.ProjectCreationFailedException;
import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.Project.SubmitType;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.project.CreateProject;
import com.google.gerrit.server.project.CreateProjectArgs;
import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.SuggestParentCandidates;
import com.google.gerrit.sshd.BaseCommand;
import com.google.inject.Inject;
@@ -29,6 +31,8 @@
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
+import java.io.PrintWriter;
+
import java.util.List;
/** Create a new project. **/
@@ -42,6 +46,10 @@
}
}
+ @Option(name = "--suggest-parents", aliases = {"-S"}, usage = "suggest parent candidates, "
+ + "if this option is used all other options and arguments are ignored")
+ private boolean suggestParent;
+
@Option(name = "--owner", aliases = {"-o"}, usage = "owner(s) of project")
private List<AccountGroup.UUID> ownerIds;
@@ -94,6 +102,9 @@
@Inject
private CreateProject.Factory CreateProjectFactory;
+ @Inject
+ private SuggestParentCandidates.Factory suggestParentCandidatesFactory;
+
@Override
public void start(final Environment env) {
startThread(new CommandRunnable() {
@@ -106,31 +117,42 @@
currentUser.getUserName());
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, msg);
}
-
+ PrintWriter p = toPrintWriter(out);
parseCommandLine();
- if (projectName == null) {
- throw new UnloggedFailure(1, "fatal: Project name is required.");
- }
-
try {
- final CreateProjectArgs args = new CreateProjectArgs();
- args.setProjectName(projectName);
- args.ownerIds = ownerIds;
- args.newParent = newParent;
- args.permissionsOnly = permissionsOnly;
- args.projectDescription = projectDescription;
- args.submitType = submitType;
- args.contributorAgreements = contributorAgreements;
- args.signedOffBy = signedOffBy;
- args.contentMerge = contentMerge;
- args.changeIdRequired = requireChangeID;
- args.branch = branch;
- args.createEmptyCommit = createEmptyCommit;
+ if (!suggestParent) {
+ if (projectName == null) {
+ throw new UnloggedFailure(1, "fatal: Project name is required.");
+ }
+ final CreateProjectArgs args = new CreateProjectArgs();
+ args.setProjectName(projectName);
+ args.ownerIds = ownerIds;
+ args.newParent = newParent;
+ args.permissionsOnly = permissionsOnly;
+ args.projectDescription = projectDescription;
+ args.submitType = submitType;
+ args.contributorAgreements = contributorAgreements;
+ args.signedOffBy = signedOffBy;
+ args.contentMerge = contentMerge;
+ args.changeIdRequired = requireChangeID;
+ args.branch = branch;
+ args.createEmptyCommit = createEmptyCommit;
- final CreateProject createProject = CreateProjectFactory.create(args);
- createProject.createProject();
+ final CreateProject createProject =
+ CreateProjectFactory.create(args);
+ createProject.createProject();
+ } else {
+ List<Project.NameKey> parentCandidates =
+ suggestParentCandidatesFactory.create().getNameKeys();
+
+ for (Project.NameKey parent : parentCandidates) {
+ p.print(parent + "\n");
+ }
+ }
} catch (ProjectCreationFailedException err) {
throw new UnloggedFailure(1, "fatal: " + err.getMessage(), err);
+ } finally {
+ p.flush();
}
}
});
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ProjectNode.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ProjectNode.java
index 47d9512..8e12571 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ProjectNode.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ProjectNode.java
@@ -16,6 +16,7 @@
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.sshd.commands.TreeFormatter.TreeNode;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
index ed5e01c..9bca0e5 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
@@ -76,11 +76,19 @@
public void run() throws Exception {
processor.setOutput(out, QueryProcessor.OutputFormat.TEXT);
parseCommandLine();
+ verifyCommandLine();
processor.query(join(query, " "));
}
});
}
+ private void verifyCommandLine() throws UnloggedFailure {
+ if (processor.getIncludeFiles() &&
+ !(processor.getIncludePatchSets() || processor.getIncludeCurrentPatchSet())) {
+ throw new UnloggedFailure(1, "--files option needs --patch-sets or --current-patch-set");
+ }
+ }
+
private static String join(List<String> list, String sep) {
StringBuilder r = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index d6306ba..8eb6058 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -14,31 +14,23 @@
package com.google.gerrit.sshd.commands;
-import com.google.gerrit.common.ChangeHookRunner;
import com.google.gerrit.common.data.ApprovalType;
import com.google.gerrit.common.data.ApprovalTypes;
import com.google.gerrit.common.data.ReviewResult;
-import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.reviewdb.Branch;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.PatchSet;
import com.google.gerrit.reviewdb.PatchSetApproval;
import com.google.gerrit.reviewdb.RevId;
import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.changedetail.AbandonChange;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.MergeOp;
-import com.google.gerrit.server.git.MergeQueue;
-import com.google.gerrit.server.git.ReplicationQueue;
-import com.google.gerrit.server.mail.AbandonedSender;
+import com.google.gerrit.server.changedetail.DeleteDraftPatchSet;
+import com.google.gerrit.server.changedetail.PublishDraft;
+import com.google.gerrit.server.changedetail.RestoreChange;
+import com.google.gerrit.server.changedetail.Submit;
import com.google.gerrit.server.mail.EmailException;
-import com.google.gerrit.server.mail.RestoredSender;
-import com.google.gerrit.server.patch.PatchSetInfoFactory;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.patch.PublishComments;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.InvalidChangeOperationException;
@@ -63,7 +55,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.TimeUnit;
public class ReviewCommand extends BaseCommand {
private static final Logger log =
@@ -123,22 +114,16 @@
private IdentifiedUser currentUser;
@Inject
- private MergeQueue merger;
-
- @Inject
- private MergeOp.Factory opFactory;
-
- @Inject
private ApprovalTypes approvalTypes;
@Inject
private ChangeControl.Factory changeControlFactory;
@Inject
- private AbandonChange.Factory abandonChangeFactory;
+ private DeleteDraftPatchSet.Factory deleteDraftPatchSetFactory;
@Inject
- private AbandonedSender.Factory abandonedSenderFactory;
+ private AbandonChange.Factory abandonChangeFactory;
@Inject
private FunctionState.Factory functionStateFactory;
@@ -147,24 +132,16 @@
private PublishComments.Factory publishCommentsFactory;
@Inject
- private RestoredSender.Factory restoredSenderFactory;
+ private PublishDraft.Factory publishDraftFactory;
@Inject
- private ChangeHookRunner hooks;
+ private RestoreChange.Factory restoreChangeFactory;
@Inject
- private GitRepositoryManager gitManager;
-
- @Inject
- private ReplicationQueue replication;
-
- @Inject
- private PatchSetInfoFactory patchSetInfoFactory;
+ private Submit.Factory submitFactory;
private List<ApproveOption> optionList;
- private Set<PatchSet.Id> toSubmit = new HashSet<PatchSet.Id>();
-
@Override
public final void start(final Environment env) {
startThread(new CommandRunnable() {
@@ -205,6 +182,9 @@
} catch (UnloggedFailure e) {
ok = false;
writeError("error: " + e.getMessage() + "\n");
+ } catch (NoSuchChangeException e) {
+ ok = false;
+ writeError("no such change " + patchSetId.getParentKey().get());
} catch (Exception e) {
ok = false;
writeError("fatal: internal server error while approving "
@@ -218,36 +198,6 @@
+ " review output above");
}
- if (!toSubmit.isEmpty()) {
- final Set<Branch.NameKey> toMerge = new HashSet<Branch.NameKey>();
- try {
- for (PatchSet.Id patchSetId : toSubmit) {
- ChangeUtil.submit(patchSetId, currentUser, db, opFactory,
- new MergeQueue() {
- @Override
- public void merge(MergeOp.Factory mof, Branch.NameKey branch) {
- toMerge.add(branch);
- }
-
- @Override
- public void schedule(Branch.NameKey branch) {
- toMerge.add(branch);
- }
-
- @Override
- public void recheckAfter(Branch.NameKey branch, long delay,
- TimeUnit delayUnit) {
- toMerge.add(branch);
- }
- });
- }
- for (Branch.NameKey branch : toMerge) {
- merger.merge(opFactory, branch);
- }
- } catch (OrmException updateError) {
- throw new Failure(1, "one or more submits failed", updateError);
- }
- }
}
});
}
@@ -257,7 +207,6 @@
final Change.Id changeId = patchSetId.getParentKey();
- ReviewResult result = null;
ChangeControl changeControl = changeControlFactory.validateFor(changeId);
if (changeComment == null) {
@@ -277,112 +226,75 @@
publishCommentsFactory.create(patchSetId, changeComment, aps, forceMessage).call();
if (abandonChange) {
- result = abandonChangeFactory.create(patchSetId, changeComment).call();
+ final ReviewResult result = abandonChangeFactory.create(
+ patchSetId, changeComment).call();
+ handleReviewResultErrors(result);
+ } else if (restoreChange) {
+ final ReviewResult result = restoreChangeFactory.create(
+ patchSetId, changeComment).call();
+ handleReviewResultErrors(result);
}
-
- if (restoreChange) {
- if (changeControl.canRestore()) {
- ChangeUtil.restore(patchSetId, currentUser, changeComment, db,
- restoredSenderFactory, hooks);
- } else {
- throw error("Not permitted to restore change");
- }
- if (submitChange) {
- changeControl = changeControlFactory.validateFor(changeId);
- }
+ if (submitChange) {
+ final ReviewResult result = submitFactory.create(patchSetId).call();
+ handleReviewResultErrors(result);
}
} catch (InvalidChangeOperationException e) {
throw error(e.getMessage());
+ } catch (IllegalStateException e) {
+ throw error(e.getMessage());
}
- if (submitChange) {
- List<SubmitRecord> submitResult = changeControl.canSubmit(db, patchSetId);
- if (submitResult.isEmpty()) {
- throw new Failure(1, "ChangeControl.canSubmit returned empty list");
- }
- switch (submitResult.get(0).status) {
- case OK:
- if (changeControl.getRefControl().canSubmit()) {
- toSubmit.add(patchSetId);
- } else {
- throw error("change " + changeId + ": you do not have submit permission");
- }
- break;
-
- case NOT_READY: {
- StringBuilder msg = new StringBuilder();
- for (SubmitRecord.Label lbl : submitResult.get(0).labels) {
- switch (lbl.status) {
- case OK:
- break;
-
- case REJECT:
- if (msg.length() > 0) msg.append("\n");
- msg.append("change " + changeId + ": blocked by " + lbl.label);
- break;
-
- case NEED:
- if (msg.length() > 0) msg.append("\n");
- msg.append("change " + changeId + ": needs " + lbl.label);
- break;
-
- case IMPOSSIBLE:
- if (msg.length() > 0) msg.append("\n");
- msg.append("change " + changeId + ": needs " + lbl.label
- + " (check project access)");
- break;
-
- default:
- throw new Failure(1, "Unsupported label status " + lbl.status);
- }
- }
- throw error(msg.toString());
- }
-
- case CLOSED:
- throw error("change " + changeId + " is closed");
-
- case RULE_ERROR:
- if (submitResult.get(0).errorMessage != null) {
- throw error("change " + changeId + ": " + submitResult.get(0).errorMessage);
- } else {
- throw error("change " + changeId + ": internal rule error");
- }
-
- default:
- throw new Failure(1, "Unsupported status " + submitResult.get(0).status);
- }
- }
if (publishPatchSet) {
- if (changeControl.isOwner() && changeControl.isVisible(db)) {
- ChangeUtil.publishDraftPatchSet(db, patchSetId);
- } else {
- throw error("Not permitted to publish draft patchset");
- }
+ final ReviewResult result = publishDraftFactory.create(patchSetId).call();
+ handleReviewResultErrors(result);
+ } else if (deleteDraftPatchSet) {
+ final ReviewResult result =
+ deleteDraftPatchSetFactory.create(patchSetId).call();
+ handleReviewResultErrors(result);
}
- if (deleteDraftPatchSet) {
- if (changeControl.isOwner() && changeControl.isVisible(db)) {
- try {
- ChangeUtil.deleteDraftPatchSet(patchSetId, gitManager, replication, patchSetInfoFactory, db);
- } catch (PatchSetInfoNotAvailableException e) {
- throw error("Error retrieving draft patchset: " + patchSetId);
- } catch (IOException e) {
- throw error("Error deleting draft patchset: " + patchSetId);
- }
- } else {
- throw error("Not permitted to delete draft patchset");
- }
- }
+ }
- if (result != null) {
- for (ReviewResult.Error resultError : result.getErrors()) {
- switch (resultError.getType()) {
- case ABANDON_NOT_PERMITTED:
- writeError("error: not permitted to abandon change");
- default:
- writeError("error: failure in review");
- }
+ private void handleReviewResultErrors(final ReviewResult result) {
+ for (ReviewResult.Error resultError : result.getErrors()) {
+ String errMsg = "error: (change " + result.getChangeId() + ") ";
+ switch (resultError.getType()) {
+ case ABANDON_NOT_PERMITTED:
+ errMsg += "not permitted to abandon change";
+ break;
+ case RESTORE_NOT_PERMITTED:
+ errMsg += "not permitted to restore change";
+ break;
+ case SUBMIT_NOT_PERMITTED:
+ errMsg += "not permitted to submit change";
+ break;
+ case SUBMIT_NOT_READY:
+ errMsg += "approvals or dependencies lacking";
+ break;
+ case CHANGE_IS_CLOSED:
+ errMsg += "change is closed";
+ break;
+ case PUBLISH_NOT_PERMITTED:
+ errMsg += "not permitted to publish change";
+ break;
+ case DELETE_NOT_PERMITTED:
+ errMsg += "not permitted to delete change/patch set";
+ break;
+ case RULE_ERROR:
+ errMsg += "rule error";
+ break;
+ case NOT_A_DRAFT:
+ errMsg += "change is not a draft";
+ break;
+ case GIT_ERROR:
+ errMsg += "error writing change to git repository";
+ break;
+ default:
+ errMsg += "failure in review";
}
+ if (resultError.getMessage() != null) {
+ errMsg += ": " + resultError.getMessage();
+ }
+ writeError(errMsg);
}
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java
index e9e6015..ff6dea9 100755
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java
@@ -14,7 +14,7 @@
package com.google.gerrit.sshd.commands;
-import com.google.gerrit.common.ChangeHookRunner;
+import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.common.ChangeListener;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.events.ChangeEvent;
@@ -43,7 +43,7 @@
private IdentifiedUser currentUser;
@Inject
- private ChangeHookRunner hooks;
+ private ChangeHooks hooks;
@Inject
@StreamCommandExecutor
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TreeFormatter.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TreeFormatter.java
index f8b518e..3aa83c3 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TreeFormatter.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TreeFormatter.java
@@ -19,6 +19,12 @@
public class TreeFormatter {
+ public static interface TreeNode {
+ public String getDisplayName();
+ public boolean isVisible();
+ public SortedSet<? extends TreeNode> getChildren();
+ }
+
public static final String NOT_VISIBLE_NODE = "(x)";
private static final String NODE_PREFIX = "|-- ";
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TreeNode.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TreeNode.java
deleted file mode 100644
index 8b7d3a9..0000000
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TreeNode.java
+++ /dev/null
@@ -1,23 +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.sshd.commands;
-
-import java.util.SortedSet;
-
-public interface TreeNode {
- public String getDisplayName();
- public boolean isVisible();
- public SortedSet<? extends TreeNode> getChildren();
-}
diff --git a/gerrit-util-cli/pom.xml b/gerrit-util-cli/pom.xml
index cfa302e..401a2ba 100644
--- a/gerrit-util-cli/pom.xml
+++ b/gerrit-util-cli/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-util-cli</artifactId>
diff --git a/gerrit-util-ssl/pom.xml b/gerrit-util-ssl/pom.xml
index 312a215..eefca62 100644
--- a/gerrit-util-ssl/pom.xml
+++ b/gerrit-util-ssl/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-util-ssl</artifactId>
diff --git a/gerrit-war/pom.xml b/gerrit-war/pom.xml
index db58ae2..ad58440 100644
--- a/gerrit-war/pom.xml
+++ b/gerrit-war/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
</parent>
<artifactId>gerrit-war</artifactId>
@@ -82,6 +82,12 @@
<dependency>
<groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-openid</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
<artifactId>gerrit-sshd</artifactId>
<version>${project.version}</version>
</dependency>
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
index 5c3a7d9..ecb0977 100644
--- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
@@ -17,8 +17,13 @@
import static com.google.inject.Scopes.SINGLETON;
import static com.google.inject.Stage.PRODUCTION;
+import com.google.gerrit.common.ChangeHookRunner;
+import com.google.gerrit.ehcache.EhcachePoolImpl;
+import com.google.gerrit.httpd.auth.openid.OpenIdModule;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.gerrit.reviewdb.AuthType;
+import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.AuthConfigModule;
import com.google.gerrit.server.config.CanonicalWebUrlModule;
import com.google.gerrit.server.config.GerritGlobalModule;
@@ -29,6 +34,7 @@
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import com.google.gerrit.server.git.PushReplication;
import com.google.gerrit.server.git.WorkQueue;
+import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
import com.google.gerrit.server.mail.SmtpEmailSender;
import com.google.gerrit.server.schema.DataSourceProvider;
import com.google.gerrit.server.schema.DatabaseModule;
@@ -183,8 +189,11 @@
private Injector createSysInjector() {
final List<Module> modules = new ArrayList<Module>();
modules.add(new WorkQueue.Module());
+ modules.add(new ChangeHookRunner.Module());
modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
+ modules.add(new EhcachePoolImpl.Module());
modules.add(new SmtpEmailSender.Module());
+ modules.add(new SignedTokenEmailTokenVerifier.Module());
modules.add(new PushReplication.Module());
modules.add(new CanonicalWebUrlModule() {
@Override
@@ -205,11 +214,17 @@
private Injector createWebInjector() {
final List<Module> modules = new ArrayList<Module>();
- modules.add(sshInjector.getInstance(WebModule.class));
modules.add(sysInjector.getInstance(GitOverHttpModule.class));
+ modules.add(sshInjector.getInstance(WebModule.class));
modules.add(sshInjector.getInstance(WebSshGlueModule.class));
modules.add(CacheBasedWebSession.module());
modules.add(HttpContactStoreConnection.module());
+
+ AuthConfig authConfig = cfgInjector.getInstance(AuthConfig.class);
+ if (authConfig.getAuthType() == AuthType.OPENID) {
+ modules.add(new OpenIdModule());
+ }
+
return sysInjector.createChildInjector(modules);
}
diff --git a/pom.xml b/pom.xml
index 312cf05..f3f7c64 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
<packaging>pom</packaging>
- <version>2.2-SNAPSHOT</version>
+ <version>2.3-SNAPSHOT</version>
<name>Gerrit Code Review - Parent</name>
<url>http://code.google.com/p/gerrit/</url>
@@ -47,7 +47,7 @@
<properties>
<jgitVersion>1.1.0.201109151100-r.141-gcd958ba</jgitVersion>
- <gwtormVersion>1.2-SNAPSHOT</gwtormVersion>
+ <gwtormVersion>1.2</gwtormVersion>
<gwtjsonrpcVersion>1.2.5</gwtjsonrpcVersion>
<gwtexpuiVersion>1.2.5</gwtexpuiVersion>
<gwtVersion>2.3.0</gwtVersion>
@@ -74,9 +74,11 @@
<module>gerrit-antlr</module>
<module>gerrit-common</module>
+ <module>gerrit-ehcache</module>
<module>gerrit-httpd</module>
<module>gerrit-launcher</module>
<module>gerrit-main</module>
+ <module>gerrit-openid</module>
<module>gerrit-pgm</module>
<module>gerrit-prettify</module>
<module>gerrit-reviewdb</module>
@@ -796,7 +798,7 @@
<dependency>
<groupId>com.googlecode.prolog-cafe</groupId>
<artifactId>PrologCafe</artifactId>
- <version>1.3-SNAPSHOT</version>
+ <version>1.3</version>
</dependency>
</dependencies>
</dependencyManagement>
diff --git a/tools/gitlog2asciidoc.py b/tools/gitlog2asciidoc.py
new file mode 100755
index 0000000..6f063b2
--- /dev/null
+++ b/tools/gitlog2asciidoc.py
@@ -0,0 +1,83 @@
+#!/usr/bin/python
+import sys
+import re
+import subprocess
+
+"""
+This script generates a release note from the output of git log
+between the specified tags.
+
+Arguments:
+since -- tag name
+until -- tag name
+
+Example Input:
+
+ * <commit subject>
+ +
+ <commit message>
+
+ Bug: issue 123
+ Change-Id: <change id>
+ Signed-off-by: <name>
+
+Expected Output:
+
+ * issue 123 <commit subject>
+ +
+ <commit message>
+"""
+
+if len(sys.argv) != 3:
+ sys.exit('Usage: ' + sys.argv[0] + ' <since> <until>')
+since_until = sys.argv[1] + '..' + sys.argv[2]
+proc = subprocess.Popen(['git', 'log', '--reverse', '--no-merges',
+ since_until, "--format=* %s%n+%n%b"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,)
+
+stdout_value = proc.communicate()[0]
+
+subject = ""
+message = []
+
+for line in stdout_value.splitlines(True):
+
+ if re.match('\* ', line) >= 0:
+ # Write change log for a commit
+ if subject != "":
+ # Write subject
+ sys.stdout.write(subject)
+
+ # Write message lines
+ if message != []:
+ # Clear + from last line in commit message
+ message[-1] = '\n'
+ for m in message:
+ sys.stdout.write(m)
+
+ # Start new commit block
+ message = []
+ subject = line
+ continue
+
+ # Move issue number to subject line
+ elif re.match('Bug: ', line) is not None:
+ line = line.replace('Bug: ', '').replace('\n',' ')
+ subject = subject[:2] + line + subject[2:]
+ # Move issue number to subject line
+ elif re.match('Issue: ', line) is not None:
+ line = line.replace('Issue: ', 'issue ').replace('\n',' ')
+ subject = subject[:2] + line + subject[2:]
+
+ # Remove commit footers
+ elif re.match(r'((\w+-)+\w+:)', line) is not None:
+ continue
+
+ else:
+ if line == '\n':
+ # Don't add extra blank line if last one is already blank
+ if message[-1] != '+\n':
+ message.append('+\n')
+ else:
+ message.append(line)