Merge "Support to inherit project level plugin configuration"
diff --git a/.buckconfig b/.buckconfig
index 171acb5..1bc29ac 100644
--- a/.buckconfig
+++ b/.buckconfig
@@ -3,11 +3,7 @@
api_deploy = //tools/maven:deploy
api_install = //tools/maven:install
docs = //Documentation:html
- download = //tools:download
- download_sources = //tools:download_sources
gerrit = //:gerrit
- eclipse = //tools/eclipse:eclipse
- eclipse_project = //tools/eclipse:eclipse_project
release = //:release
[buildfile]
diff --git a/.buckversion b/.buckversion
index 4b453f2..ca3c391 100644
--- a/.buckversion
+++ b/.buckversion
@@ -1 +1 @@
-5fc60079d9dbaaf8a1e7d542dcb21fd901f68245
+4464beabf38ba9c329918b9c7391064bdb2d3fe9
diff --git a/BUCK b/BUCK
index b1a0c75..616a0fe 100644
--- a/BUCK
+++ b/BUCK
@@ -15,9 +15,12 @@
genrule(
name = 'api',
- cmd = '',
+ cmd = ';'.join(
+ ['cd $TMP'] +
+ ['ln -s $(location %s) .' % n for n in API_DEPS] +
+ ['zip -q0 $OUT *']),
deps = API_DEPS,
- out = '__fake.api__',
+ out = 'api.zip',
)
java_binary(
diff --git a/Documentation/BUCK b/Documentation/BUCK
index faa2553..2876f13 100644
--- a/Documentation/BUCK
+++ b/Documentation/BUCK
@@ -4,48 +4,41 @@
MAIN = ['//gerrit-pgm:pgm', '//gerrit-gwtui:ui_module']
SRCS = glob(['*.txt'], excludes = ['licenses.txt'])
-HTML = [txt[0:-4] + '.html' for txt in SRCS]
genrule(
name = 'html',
cmd = 'cd $TMP;' +
'mkdir -p Documentation/images;' +
+ 'unzip -q $SRCDIR/only_html.zip -d Documentation/;' +
'for s in $SRCS;do ln -s $s Documentation;done;' +
'mv Documentation/*.{jpg,png} Documentation/images;' +
+ 'rm Documentation/only_html.zip;' +
'rm Documentation/licenses.txt;' +
'cp $SRCDIR/licenses.txt LICENSES.txt;' +
'zip -qr $OUT *',
- srcs = [genfile(d) for d in HTML] +
+ srcs = [genfile('only_html.zip')] +
glob([
'images/*.jpg',
'images/*.png',
]) + [
- genfile('doc.css'),
- genfile('licenses.html'),
+ 'doc.css',
genfile('licenses.txt'),
],
- deps = [':' + d for d in HTML] + [
- ':licenses.html',
+ deps = [
+ ':generate_html',
':licenses.txt',
- ':doc.css',
],
out = 'html.zip',
visibility = ['PUBLIC'],
)
-genrule(
- name = 'doc.css',
- cmd = 'ln -s $SRCDIR/doc.css $OUT',
- srcs = ['doc.css'],
- out = 'doc.css',
-)
-
genasciidoc(
+ name = 'generate_html',
srcs = SRCS + [genfile('licenses.txt')],
- outs = HTML + ['licenses.html'],
- deps = DOCUMENTATION_DEPS,
+ deps = [':licenses.txt'],
attributes = documentation_attributes(git_describe()),
backend = 'html5',
+ out = 'only_html.zip',
)
genrule(
diff --git a/Documentation/asciidoc.defs b/Documentation/asciidoc.defs
index 8279847..e2de785 100644
--- a/Documentation/asciidoc.defs
+++ b/Documentation/asciidoc.defs
@@ -13,25 +13,30 @@
# limitations under the License.
def genasciidoc(
+ name,
+ out,
srcs = [],
- outs = [],
- deps = {},
+ deps = [],
attributes = [],
backend = None,
visibility = []):
EXPN = '.expn'
- asciidoc = ['$(exe //lib/asciidoctor:asciidoc)']
+ asciidoc = [
+ '$(exe //lib/asciidoctor:asciidoc)',
+ '-z', '$OUT',
+ '--in-ext', '".txt%s"' % EXPN,
+ '--out-ext', '".html"',
+ ]
if backend:
asciidoc.extend(['-b', backend])
for attribute in attributes:
asciidoc.extend(['-a', attribute])
- asciidoc.extend(['-o', '$OUT'])
+ asciidoc.append('$SRCS')
+ newsrcs = []
+ newdeps = deps + ['//lib/asciidoctor:asciidoc']
- for p in zip(srcs, outs):
- src, out = p
- dep = deps.get(src) or []
-
+ for src in srcs:
tx = []
fn = src
if fn.startswith('BUCKGEN:') :
@@ -48,14 +53,14 @@
deps = tx + [':replace_macros'],
out = ex,
)
- genrule(
- name = out,
- cmd = ' '.join(asciidoc + ['$SRCDIR/' + ex]),
- srcs = [genfile(ex)] + [genfile(n + EXPN) for n in dep],
- deps = [':' + n + EXPN for n in dep] + [
- ':' + ex,
- '//lib/asciidoctor:asciidoc',
- ],
- out = out,
- visibility = visibility,
- )
+ newdeps.append(':' + ex)
+ newsrcs.append(genfile(ex))
+
+ genrule(
+ name = name,
+ cmd = ' '.join(asciidoc),
+ srcs = newsrcs,
+ deps = newdeps,
+ out = out,
+ visibility = visibility,
+ )
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 339b0c0..19749a3 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1334,6 +1334,7 @@
----
fetch-cmd some://url/to/commit-msg .git/hooks/commit-msg ; chmod +x .git/hooks/commit-msg
----
+
+
By default unset; falls back to using scp from the canonical SSH host,
or curl from the canonical HTTP URL for the server. Only necessary if a
@@ -1357,6 +1358,11 @@
Code Review's own bug tracker but could be directed to the system
administrator's ticket queue.
+[[gerrit.changeScreen]]gerrit.changeScreen::
++
+Default change screen UI to direct users to. Valid values are
+`OLD_UI` and `CHANGE_SCREEN2`. Default is `OLD_UI`.
+
[[gitweb]]Section gitweb
~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/dev-buck.txt b/Documentation/dev-buck.txt
index 74f56b3..a41a821 100644
--- a/Documentation/dev-buck.txt
+++ b/Documentation/dev-buck.txt
@@ -53,10 +53,10 @@
Generating the Eclipse Project
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Create the Eclipse project by building the `eclipse` target:
+Create the Eclipse project:
----
- buck build eclipse
+ tools/eclipse/project.py
----
In Eclipse, choose 'Import existing project' and select the `gerrit` project
@@ -65,21 +65,20 @@
Expand the `gerrit` project, right-click on the `buck-out` folder, select
'Properties', and then under 'Attributes' check 'Derived'.
-Note that if you make any changes in the project configuration that get
-saved to the `.project` file, for example adding Resource Filters on a
-folder, they will be overwritten the next time you run `buck build eclipse`.
+Note that if you make any changes in the project configuration
+that get saved to the `.project` file, for example adding Resource
+Filters on a folder, they will be overwritten the next time you run
+`tools/eclipse/project.py`.
Refreshing the Classpath
~~~~~~~~~~~~~~~~~~~~~~~~
-Normally `buck build eclipse` does everything necessary to generate a working Eclipse
-environment, but if the code doesn't compile and an updated classpath is needed, the
-Eclipse project can be refreshed and missing dependency JARs can be downloaded by
-building the `eclipse_project` and `download` targets:
+If an updated classpath is needed, the Eclipse project can be
+refreshed and missing dependency JARs can be downloaded:
----
- buck build eclipse_project download
+ tools/eclipse/project.py
----
@@ -92,7 +91,7 @@
show documentation or dive into the implementation of a library JAR:
----
- buck build download_sources
+ tools/eclipse/project.py --src
----
@@ -250,12 +249,10 @@
Dependency JARs are normally downloaded automatically, but Buck can inspect
its graph and download any missing JAR files. This is useful to enable
-subsequent builds to run without network access.
-
-Force a download of dependency JARs by building the `download` target:
+subsequent builds to run without network access:
----
- buck build download
+ tools/download_all.py
----
When downloading from behind a proxy (which is common in some corporate
@@ -335,6 +332,31 @@
[-] BUILDING...FINISHED 0.2s
----
+Overwrite Buck's settings
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the latest version of Buck the wrapper script `buck_common` will source
+one of these files (if they exist): /etc/buck.conf, $HOME/.buck/buck.conf or
+$HOME/.buckrc. The trivial case to overwrite the Buck's default 1GB heap size:
+
+----
+ cat > $HOME/.buckrc <<EOF
+ export BUCK_EXTRA_JAVA_ARGS="\
+ -XX:MaxPermSize=512m \
+ -Xms8000m \
+ -Xmx16000m"
+ EOF
+----
+
+Or to debug BUCK, set BUCK_DEBUG_MODE to anything non-empty, then connect to
+port 8888:
+
+----
+ cat > $HOME/.buckrc <<EOF
+ export BUCK_DEBUG_MODE="yes"
+ EOF
+----
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index cc96e81..5ab38b8 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -697,6 +697,41 @@
"Bonjour François from change 1, patch set 1!"
====
+A special case is to bind an endpoint without a view name. This is
+particularly useful for DELETE requests:
+
+[source,java]
+----
+public class Module extends AbstractModule {
+ @Override
+ protected void configure() {
+ install(new RestApiModule() {
+ @Override
+ protected void configure() {
+ delete(PROJECT_KIND)
+ .to(DeleteProject.class);
+ }
+ });
+ }
+}
+----
+
+For a `UiAction` bound this way, a JS API function can be provided.
+
+Currently only one restriction exists: per plugin only one `UiAction`
+can be bound per resource without view name. To define a JS function
+for the `UiAction`, "/" must be used as the name:
+
+[source,javascript]
+----
+Gerrit.install(function(self) {
+ function onDeleteProject(c) {
+ [...]
+ }
+ self.onAction('project', '/', onDeleteProject);
+});
+----
+
[[http]]
HTTP Servlets
-------------
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 65dd3a4..bc5d0bd 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -1020,6 +1020,48 @@
]
----
+[[suggest-reviewers]]
+Suggest Reviewers
+~~~~~~~~~~~~~~~~~
+[verse]
+'GET /changes/link:#change-id[\{change-id\}]/suggest_reviewers?q=J&n=5'
+
+Suggest the reviewers for a given query `q` and result limit `n`. If result
+limit is not passed, then the default 10 is used.
+
+As result a list of link:#suggested-reviewer-info[SuggestedReviewerInfo] entries is returned.
+
+.Request
+----
+ GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/suggest_reviewers?q=J HTTP/1.0
+----
+
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json;charset=UTF-8
+
+ )]}'
+ [
+ {
+ "kind": "gerritcodereview#suggestedreviewer",
+ "account": {
+ "_account_id": 1000097,
+ "name": "Jane Roe",
+ "email": "jane.roe@example.com"
+ }
+ },
+ {
+ "kind": "gerritcodereview#suggestedreviewer",
+ "group": {
+ "id": "4fd581c0657268f2bdcc26699fbf9ddb76e3a279",
+ "name": "Joiner"
+ }
+ }
+ ]
+----
+
[[get-reviewer]]
Get Reviewer
~~~~~~~~~~~~
@@ -2540,6 +2582,18 @@
The time and date describing when the approval was made.
|===========================
+[[group-base-info]]
+GroupBaseInfo
+~~~~~~~~~~~~~
+The `GroupBaseInfo` entity contains base information about the group.
+
+[options="header",width="50%",cols="1,6"]
+|==========================
+|Field Name |Description
+|`id` |The id of the group.
+|`name` |The name of the group.
+|==========================
+
[[change-info]]
ChangeInfo
~~~~~~~~~~
@@ -3118,6 +3172,17 @@
to return results from the input rule.
|===========================
+[[suggested-reviewer-info]]
+SuggestedReviewerInfo
+~~~~~~~~~~~~~~~~~~~~~
+The `SuggestedReviewerInfo` entity contains information about a reviewer
+that can be added to a change (an account or a group).
+
+`SuggestedReviewerInfo` has either the `account` field that contains
+the link:rest-api-accounts.html#account-info[AccountInfo] entity, or
+the `group` field that contains the
+link:rest-api-changes.html#group-base-info[GroupBaseInfo] entity.
+
[[submit-info]]
SubmitInfo
~~~~~~~~~~
diff --git a/ReleaseNotes/ReleaseNotes-2.6.2.txt b/ReleaseNotes/ReleaseNotes-2.6.2.txt
deleted file mode 100644
index af00a71..0000000
--- a/ReleaseNotes/ReleaseNotes-2.6.2.txt
+++ /dev/null
@@ -1,67 +0,0 @@
-Release notes for Gerrit 2.6.2
-==============================
-
-Gerrit 2.6.2 is now available:
-
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.6.2.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.6.2.war]
-
-There are no schema changes from 2.6.1.
-
-However, if upgrading from a version older than 2.6, follow the upgrade
-procedure in the 2.6 link:ReleaseNotes-2.6.html[Release Notes].
-
-
-Bug Fixes
----------
-
-
-* Fix null-pointer exception when dashboard title is not specified.
-+
-If the title is not specified, the path of the dashboard config file
-is used as title.
-
-* Properly handle double-click on external group in GroupTable.
-+
-Double-clicking on an external group opens the group's URL (if it
-is provided).
-
-* link:https://code.google.com/p/gerrit/issues/detail?id=1872[Issue 1872]:
-Fix tab expansion in diff screens when syntax coloring is on.
-
-* link:https://code.google.com/p/gerrit/issues/detail?id=1904[Issue 1904]:
-Fix diff screens for files with CRLF line endings.
-
-* Allow label values to be configured with no text.
-
-* link:https://code.google.com/p/gerrit/issues/detail?id=1966[Issue 1966]:
-Fix Gerrit plugins under Tomcat by avoiding Guice static filter.
-
-* link:https://code.google.com/p/gerrit/issues/detail?id=2010[Issue 2010]:
-Fix null-pointer exception when searching for changes with the query
-`owner:self`.
-
-* link:https://code.google.com/p/gerrit/issues/detail?id=2039[Issue 2039]:
-Fix browser null-pointer exception when ChangeCache is incomplete.
-
-* link:https://code.google.com/p/gerrit/issues/detail?id=2054[Issue 2054]:
-Expand capabilities of `ldap.groupMemberPattern`.
-
-* link:https://code.google.com/p/gerrit/issues/detail?id=2056[Issue 2056]:
-Display custom NoOp label score for open changes.
-
-* link:https://code.google.com/p/gerrit/issues/detail?id=2093[Issue 2093]:
-Fix incorrect title of "repo download" link on change screen.
-
-* link:https://code.google.com/p/gerrit/issues/detail?id=2098[Issue 2098]:
-Fix re-enabling of disabled plugins.
-
-* link:https://code.google.com/p/gerrit/issues/detail?id=2127[Issue 2127]:
-Remove hard-coded documentation links from the admin page.
-
-* link:https://code.google.com/p/gerrit/issues/detail?id=2128[Issue 2128]:
-Fix null-pointer exception when deleting draft patch set when previous
-draft was already deleted.
-
-No other changes since 2.6.1.
-
diff --git a/ReleaseNotes/ReleaseNotes-2.7.txt b/ReleaseNotes/ReleaseNotes-2.7.txt
index 348b812..3a2ebf9 100644
--- a/ReleaseNotes/ReleaseNotes-2.7.txt
+++ b/ReleaseNotes/ReleaseNotes-2.7.txt
@@ -4,12 +4,11 @@
Gerrit 2.7 is now available:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.7-rc2.war[https://gerrit-releases.storage.googleapis.com/gerrit-2.7-rc2.war]
+link:https://gerrit-releases.storage.googleapis.com/gerrit-2.7.war[
+https://gerrit-releases.storage.googleapis.com/gerrit-2.7.war]
-Gerrit 2.7 includes the bug fixes done with
-link:ReleaseNotes-2.6.1.html[Gerrit 2.6.1] and
-link:ReleaseNotes-2.6.2.html[Gerrit 2.6.2]. These bug fixes are *not*
-listed in these release notes.
+Gerrit 2.7 includes the bug fixes done with link:ReleaseNotes-2.6.1.html[Gerrit 2.6.1].
+These bug fixes are *not* listed in these release notes.
Schema Change
-------------
@@ -207,23 +206,49 @@
* Postpone check for first account until adding an account.
-* Mark `ALREADY_MERGED` changes as merged in the database.
+* link:https://code.google.com/p/gerrit/issues/detail?id=1848[Issue 1848]:
+Mark `ALREADY_MERGED` changes as merged in the database.
+
If a change was marked `ALREADY_MERGED`, likely due to a bug in
merge code, it does not end up in the list of changes to be submitted
and never gets marked as merged despite the branch head already
having advanced.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=600[Issue 600]:
+Fix change stuck in SUBMITTED state but actually merged.
+
-Works around (but does not fix)
-link:https://code.google.com/p/gerrit/issues/detail?id=1985[Issue 1985] and
-link:https://code.google.com/p/gerrit/issues/detail?id=600[Issue 600]
-to allow recovery from certain kind of bad state.
+When submitting a commit that has a tag, it could not be merged.
+
+* Fix null-pointer exception when dashboard title is not specified.
++
+If the title is not specified, the path of the dashboard config file
+is used as title.
+
+* Allow label values to be configured with no text.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=1966[Issue 1966]:
+Fix Gerrit plugins under Tomcat by avoiding Guice static filter.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2054[Issue 2054]:
+Expand capabilities of `ldap.groupMemberPattern`.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2098[Issue 2098]:
+Fix re-enabling of disabled plugins.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2128[Issue 2128]:
+Fix null-pointer exception when deleting draft patch set when previous
+draft was already deleted.
Web UI
~~~~~~
+* Properly handle double-click on external group in GroupTable.
++
+Double-clicking on an external group opens the group's URL (if it
+is provided).
+
* link:https://code.google.com/p/gerrit/issues/detail?id=1848[Issue 1848]:
Don't discard inline comments when escape key is pressed.
@@ -234,6 +259,28 @@
correct display of bold text on Mac OS X. Simplify the selector to sans-serif
and allow the browser to use the user's preferred font in this family.
+* link:https://code.google.com/p/gerrit/issues/detail?id=1872[Issue 1872]:
+Fix tab expansion in diff screens when syntax coloring is on.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=1904[Issue 1904]:
+Fix diff screens for files with CRLF line endings.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2056[Issue 2056]:
+Display custom NoOp label score for open changes.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2093[Issue 2093]:
+Fix incorrect title of "repo download" link on change screen.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2127[Issue 2127]:
+Remove hard-coded documentation links from the admin page.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2010[Issue 2010]:
+Fix null-pointer exception when searching for changes with the query
+`owner:self`.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2039[Issue 2039]:
+Fix browser null-pointer exception when ChangeCache is incomplete.
+
REST API
~~~~~~~~
diff --git a/ReleaseNotes/ReleaseNotes-2.8.txt b/ReleaseNotes/ReleaseNotes-2.8.txt
index 1c63983f..c595b5b 100644
--- a/ReleaseNotes/ReleaseNotes-2.8.txt
+++ b/ReleaseNotes/ReleaseNotes-2.8.txt
@@ -21,23 +21,44 @@
a later 2.1.x version), and then to 2.8.x. If you are upgrading from 2.2.x.x or
later, you may ignore this warning and upgrade directly to 2.8.x.
+*WARNING:* The replication plugin now automatically creates missing repositories
+on the destination if during the replication of a ref the target repository is
+found to be missing. This is a change in behavior of the replication plugin. To go
+back to the old behavior, set the parameter `remote.NAME.createMissingRepositories`
+in the `replication.config` file to `false`.
+
Release Highlights
------------------
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/config-gerrit.html#index[
+Secondary indexing with Lucene and Solr].
+
* Lots of new link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/rest-api.html[
REST API endpoints].
-* New link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/js-api.html[
-JavaScript API].
+* New
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/dev-plugins.html#ui_extension[
+UI extension] and
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/js-api.html[
+JavaScript API] for plugins.
-* New build system using link:http://facebook.github.io/buck/[Facebook Buck].
+* New build system using Facebook's link:http://facebook.github.io/buck/[Buck].
New Features
------------
+Build
+~~~~~
+
+* Gerrit is now built with
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/dev-buck.html[
+Buck].
+
+* Documentation is now built with Buck and link:http://asciidoctor.org[Asciidoctor].
+
Configuration
~~~~~~~~~~~~~
@@ -79,6 +100,12 @@
link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/database-setup.html#createdb_oracle[
Oracle database].
+* New bash completion script for autocompletion of parameters to the gerrit.sh wrapper.
+
+* The site can be
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/config-auto-site-initialization.html[
+auto-initialized on server startup].
+
Web UI
~~~~~~
@@ -219,6 +246,12 @@
* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/rest-api-changes.html#get-commit[
Get parsed commit of a revision]
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/rest-api-changes.html#publish-draft-change[
+Publish draft change]
+
+* link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/rest-api-changes.html#delete-draft-change[
+Delete draft change]
+
Config
^^^^^^
@@ -297,6 +330,8 @@
* Plugins may now contribute buttons to various parts of the UI using the
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/dev-plugins.html#ui_extension[
+UI extension] and
link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/js-api.html[
JavaScript API].
@@ -313,9 +348,22 @@
link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/dev-plugins.html#capabilities[
Global capabilities].
+* Plugins may now
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/dev-plugins.html#plugin_name[
+define their own name].
+
* The "hello world" plugin is replaced with the "cookbook plugin" which has more
examples of the plugin API's usage.
+* Plugins may now trigger and listen to a "project deleted"
+link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.8/dev-plugins.html#events[
+event].
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2101[Issue 2101]:
+Plugins implementing LifecycleListener can use auto registration.
+
+* Plugins may bind REST endpoints with empty view names.
+
Commit Message Length Checker
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -326,6 +374,21 @@
Replication
^^^^^^^^^^^
+* Automatically create missing repositories on the destination.
++
+If during the replication of a ref the target repository is found to be missing,
+the repository is automatically created.
++
+This is a change in behavior of the replication plugin. To go back to the old
+behavior, set the parameter `remote.NAME.createMissingRepositories` in the
+`replication.config` file to `false`.
+
+* Support for replication of project deletions.
++
+The replication plugin can now be configured to listen to project deletion events
+and to replicate the project deletions. By default project deletions are *not*
+replicated.
+
* The `{$name}` placeholder is optional when replicating a single project,
allowing a single project to be replicated under a different name.
@@ -344,6 +407,19 @@
* The `startReplication` global capability is now provided by the plugin.
+* Pushes to each destination URI are serialized.
++
+Scheduling a retry to avoid collision with an in-flight push is differentiated
+from a retry due to a transport error. In the case of collision avoidance, the
+job is rescheduled according to the replication delay, rather than the retry
+delay.
+
+Review Notes
+^^^^^^^^^^^^
+
+* Do not try to create review notes for ref deletion events.
+
+
ssh
~~~
@@ -395,6 +471,12 @@
* link:https://code.google.com/p/gerrit/issues/detail?id=600[Issue 600]:
Fix change stuck in SUBMITTED state but actually merged.
+* link:https://code.google.com/p/gerrit/issues/detail?id=1699[Issue 1699]:
+Fix handling of projects with trailing ".git" suffix.
+
+* Limit retrying of submitted changes to 12 hours.
+
+
Configuration
~~~~~~~~~~~~~
@@ -495,6 +577,8 @@
* The `@CommandMetaData(descr)` annotation is deprecated in favor of `@CommandMetaData(description)`.
+* Improve the error message when rejecting upload for review to a read-only project.
+
Emails
~~~~~~
diff --git a/ReleaseNotes/index.txt b/ReleaseNotes/index.txt
index c6c45eb..28b9f65 100644
--- a/ReleaseNotes/index.txt
+++ b/ReleaseNotes/index.txt
@@ -14,7 +14,6 @@
[[2_6]]
Version 2.6.x
-------------
-* link:ReleaseNotes-2.6.2.html[2.6.2]
* link:ReleaseNotes-2.6.1.html[2.6.1]
* link:ReleaseNotes-2.6.html[2.6]
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/GitUtil.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/GitUtil.java
index 5f8faff..c17598f 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/GitUtil.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/GitUtil.java
@@ -25,6 +25,7 @@
import com.jcraft.jsch.Session;
import org.eclipse.jgit.api.AddCommand;
+import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.CommitCommand;
import org.eclipse.jgit.api.Git;
@@ -33,6 +34,7 @@
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.JschConfigSessionFactory;
@@ -130,47 +132,58 @@
addCmd.call();
}
- public static String createCommit(Git git, PersonIdent i, String msg)
+ public static Commit createCommit(Git git, PersonIdent i, String msg)
throws GitAPIException, IOException {
- return createCommit(git, i, msg, true, false);
+ return createCommit(git, i, msg, null);
}
- public static void amendCommit(Git git, PersonIdent i, String msg, String changeId)
+ public static Commit amendCommit(Git git, PersonIdent i, String msg, String changeId)
throws GitAPIException, IOException {
msg = ChangeIdUtil.insertId(msg, ObjectId.fromString(changeId.substring(1)));
- createCommit(git, i, msg, false, true);
+ return createCommit(git, i, msg, changeId);
}
- private static String createCommit(Git git, PersonIdent i, String msg,
- boolean insertChangeId, boolean amend) throws GitAPIException, IOException {
- ObjectId changeId = null;
- if (insertChangeId) {
- changeId = computeChangeId(git, i, msg);
- msg = ChangeIdUtil.insertId(msg, changeId);
- }
+ private static Commit createCommit(Git git, PersonIdent i, String msg,
+ String changeId) throws GitAPIException, IOException {
final CommitCommand commitCmd = git.commit();
- commitCmd.setAmend(amend);
+ commitCmd.setAmend(changeId != null);
commitCmd.setAuthor(i);
commitCmd.setCommitter(i);
- commitCmd.setMessage(msg);
- commitCmd.call();
- return changeId != null ? "I" + changeId.getName() : null;
+ if (changeId == null) {
+ ObjectId id = computeChangeId(git, i, msg);
+ changeId = "I" + id.getName();
+ }
+ msg = ChangeIdUtil.insertId(msg, ObjectId.fromString(changeId.substring(1)));
+ commitCmd.setMessage(msg);
+
+ RevCommit c = commitCmd.call();
+ return new Commit(c, changeId);
}
private static ObjectId computeChangeId(Git git, PersonIdent i, String msg)
throws IOException {
RevWalk rw = new RevWalk(git.getRepository());
try {
- RevCommit parent =
- rw.lookupCommit(git.getRepository().getRef(Constants.HEAD).getObjectId());
- return ChangeIdUtil.computeChangeId(parent.getTree(), parent.getId(), i, i, msg);
+ Ref head = git.getRepository().getRef(Constants.HEAD);
+ if (head.getObjectId() != null) {
+ RevCommit parent = rw.lookupCommit(head.getObjectId());
+ return ChangeIdUtil.computeChangeId(parent.getTree(), parent.getId(), i, i, msg);
+ } else {
+ return ChangeIdUtil.computeChangeId(null, null, i, i, msg);
+ }
} finally {
rw.release();
}
}
+ public static void checkout(Git git, String name) throws GitAPIException {
+ CheckoutCommand checkout = git.checkout();
+ checkout.setName(name);
+ checkout.call();
+ }
+
public static PushResult pushHead(Git git, String ref, boolean pushTags)
throws GitAPIException {
PushCommand pushCmd = git.push();
@@ -181,4 +194,22 @@
Iterable<PushResult> r = pushCmd.call();
return Iterables.getOnlyElement(r);
}
+
+ public static class Commit {
+ private final RevCommit commit;
+ private final String changeId;
+
+ Commit(RevCommit commit, String changeId) {
+ this.commit = commit;
+ this.changeId = changeId;
+ }
+
+ public RevCommit getCommit() {
+ return commit;
+ }
+
+ public String getChangeId() {
+ return changeId;
+ }
+ }
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/PushOneCommit.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/PushOneCommit.java
index 7450565..1c0fafe 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/PushOneCommit.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/PushOneCommit.java
@@ -27,6 +27,7 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.git.GitUtil.Commit;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
@@ -36,7 +37,9 @@
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
@@ -82,15 +85,17 @@
public Result to(Git git, String ref)
throws GitAPIException, IOException {
add(git, fileName, content);
+ Commit c;
if (changeId != null) {
- amendCommit(git, i, subject, changeId);
+ c = amendCommit(git, i, subject, changeId);
} else {
- changeId = createCommit(git, i, subject);
+ c = createCommit(git, i, subject);
+ changeId = c.getChangeId();
}
if (tagName != null) {
git.tag().setName(tagName).setAnnotated(false).call();
}
- return new Result(db, ref, pushHead(git, ref, tagName != null), changeId, subject);
+ return new Result(db, ref, pushHead(git, ref, tagName != null), c, subject);
}
public void setTag(final String tagName) {
@@ -101,32 +106,40 @@
private final ReviewDb db;
private final String ref;
private final PushResult result;
- private final String changeId;
+ private final Commit commit;
private final String subject;
- private Result(ReviewDb db, String ref, PushResult result, String changeId,
+ private Result(ReviewDb db, String ref, PushResult result, Commit commit,
String subject) {
this.db = db;
this.ref = ref;
this.result = result;
- this.changeId = changeId;
+ this.commit = commit;
this.subject = subject;
}
public PatchSet.Id getPatchSetId() throws OrmException {
return Iterables.getOnlyElement(
- db.changes().byKey(new Change.Key(changeId))).currentPatchSetId();
+ db.changes().byKey(new Change.Key(commit.getChangeId()))).currentPatchSetId();
}
public String getChangeId() {
- return changeId;
+ return commit.getChangeId();
+ }
+
+ public ObjectId getCommitId() {
+ return commit.getCommit().getId();
+ }
+
+ public RevCommit getCommit() {
+ return commit.getCommit();
}
public void assertChange(Change.Status expectedStatus,
String expectedTopic, TestAccount... expectedReviewers)
throws OrmException {
Change c =
- Iterables.getOnlyElement(db.changes().byKey(new Change.Key(changeId)).toList());
+ Iterables.getOnlyElement(db.changes().byKey(new Change.Key(commit.getChangeId())).toList());
assertEquals(subject, c.getSubject());
assertEquals(expectedStatus, c.getStatus());
assertEquals(expectedTopic, Strings.emptyToNull(c.getTopic()));
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
new file mode 100644
index 0000000..5209a0c
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -0,0 +1,256 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+import static com.google.gerrit.acceptance.git.GitUtil.cloneProject;
+import static com.google.gerrit.acceptance.git.GitUtil.initSsh;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.AccountCreator;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.RestSession;
+import com.google.gerrit.acceptance.SshSession;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.git.GitUtil;
+import com.google.gerrit.acceptance.git.PushOneCommit;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.client.Project.InheritableBoolean;
+import com.google.gerrit.reviewdb.client.Project.SubmitType;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.google.gwtorm.server.SchemaFactory;
+import com.google.inject.Inject;
+
+import com.jcraft.jsch.JSchException;
+
+import org.apache.http.HttpStatus;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.diff.DiffFormatter;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public abstract class AbstractSubmit extends AbstractDaemonTest {
+
+ @Inject
+ private AccountCreator accounts;
+
+ @Inject
+ private SchemaFactory<ReviewDb> reviewDbProvider;
+
+ @Inject
+ private GitRepositoryManager repoManager;
+
+ protected RestSession session;
+
+ private TestAccount admin;
+ private Project.NameKey project;
+ private ReviewDb db;
+
+ @Before
+ public void setUp() throws Exception {
+ admin = accounts.admin();
+ session = new RestSession(server, admin);
+ initSsh(admin);
+
+ project = new Project.NameKey("p");
+
+ db = reviewDbProvider.open();
+ }
+
+ @After
+ public void cleanup() {
+ db.close();
+ }
+
+ protected abstract SubmitType getSubmitType();
+
+ @Test
+ public void submitToEmptyRepo() throws JSchException, IOException,
+ GitAPIException {
+ Git git = createProject(false);
+ PushOneCommit.Result change = createChange(git);
+ submit(change.getChangeId());
+ assertEquals(change.getCommitId(), getRemoteHead().getId());
+ }
+
+ protected Git createProject() throws JSchException, IOException,
+ GitAPIException {
+ return createProject(true);
+ }
+
+ private Git createProject(boolean emptyCommit)
+ throws JSchException, IOException, GitAPIException {
+ SshSession sshSession = new SshSession(server, admin);
+ try {
+ GitUtil.createProject(sshSession, project.get(), null, emptyCommit);
+ setSubmitType(getSubmitType());
+ return cloneProject(sshSession.getUrl() + "/" + project.get());
+ } finally {
+ sshSession.close();
+ }
+ }
+
+ private void setSubmitType(SubmitType submitType) throws IOException {
+ ProjectConfigInput in = new ProjectConfigInput();
+ in.submit_type = submitType;
+ in.use_content_merge = InheritableBoolean.FALSE;
+ RestResponse r = session.put("/projects/" + project.get() + "/config", in);
+ assertEquals(HttpStatus.SC_OK, r.getStatusCode());
+ r.consume();
+ }
+
+ protected void setUseContentMerge() throws IOException {
+ ProjectConfigInput in = new ProjectConfigInput();
+ in.use_content_merge = InheritableBoolean.TRUE;
+ RestResponse r = session.put("/projects/" + project.get() + "/config", in);
+ assertEquals(HttpStatus.SC_OK, r.getStatusCode());
+ r.consume();
+ }
+
+ protected PushOneCommit.Result createChange(Git git) throws GitAPIException,
+ IOException {
+ PushOneCommit push = new PushOneCommit(db, admin.getIdent());
+ return push.to(git, "refs/for/master");
+ }
+
+ protected PushOneCommit.Result createChange(Git git, String subject,
+ String fileName, String content) throws GitAPIException, IOException {
+ PushOneCommit push =
+ new PushOneCommit(db, admin.getIdent(), subject, fileName, content);
+ return push.to(git, "refs/for/master");
+ }
+
+ protected void submit(String changeId) throws IOException {
+ submit(changeId, HttpStatus.SC_OK);
+ }
+
+ protected void submitWithConflict(String changeId) throws IOException {
+ submit(changeId, HttpStatus.SC_CONFLICT);
+ }
+
+ private void submit(String changeId, int expectedStatus) throws IOException {
+ approve(changeId);
+ RestResponse r =
+ session.post("/changes/" + changeId + "/submit",
+ SubmitInput.waitForMerge());
+ assertEquals(expectedStatus, r.getStatusCode());
+ if (expectedStatus == HttpStatus.SC_OK) {
+ ChangeInfo change =
+ (new Gson()).fromJson(r.getReader(),
+ new TypeToken<ChangeInfo>() {}.getType());
+ assertEquals("MERGED", change.status);
+ }
+ r.consume();
+ }
+
+ private void approve(String changeId) throws IOException {
+ RestResponse r =
+ session.post("/changes/" + changeId + "/revisions/current/review",
+ ReviewInput.approve());
+ assertEquals(HttpStatus.SC_OK, r.getStatusCode());
+ r.consume();
+ }
+
+ protected void assertCherryPick(Git localGit, boolean contentMerge) throws IOException {
+ assertRebase(localGit, contentMerge);
+ RevCommit remoteHead = getRemoteHead();
+ assertFalse(remoteHead.getFooterLines("Reviewed-On").isEmpty());
+ assertFalse(remoteHead.getFooterLines("Reviewed-By").isEmpty());
+ }
+
+ protected void assertRebase(Git localGit, boolean contentMerge) throws IOException {
+ Repository repo = localGit.getRepository();
+ RevCommit localHead = getHead(repo);
+ RevCommit remoteHead = getRemoteHead();
+ assertNotEquals(localHead.getId(), remoteHead.getId());
+ assertEquals(1, remoteHead.getParentCount());
+ if (!contentMerge) {
+ assertEquals(getLatestDiff(repo), getLatestRemoteDiff());
+ }
+ assertEquals(localHead.getShortMessage(), remoteHead.getShortMessage());
+ }
+
+ private RevCommit getHead(Repository repo) throws IOException {
+ return getHead(repo, "HEAD");
+ }
+
+ protected RevCommit getRemoteHead() throws IOException {
+ Repository repo = repoManager.openRepository(project);
+ try {
+ return getHead(repo, "refs/heads/master");
+ } finally {
+ repo.close();
+ }
+ }
+
+ private RevCommit getHead(Repository repo, String name) throws IOException {
+ try {
+ RevWalk rw = new RevWalk(repo);
+ try {
+ return rw.parseCommit(repo.getRef(name).getObjectId());
+ } finally {
+ rw.release();
+ }
+ } finally {
+ repo.close();
+ }
+ }
+
+ private String getLatestDiff(Repository repo) throws IOException {
+ ObjectId oldTreeId = repo.resolve("HEAD~1^{tree}");
+ ObjectId newTreeId = repo.resolve("HEAD^{tree}");
+ return getLatestDiff(repo, oldTreeId, newTreeId);
+ }
+
+ private String getLatestRemoteDiff() throws IOException {
+ Repository repo = repoManager.openRepository(project);
+ try {
+ RevWalk rw = new RevWalk(repo);
+ try {
+ ObjectId oldTreeId = repo.resolve("refs/heads/master~1^{tree}");
+ ObjectId newTreeId = repo.resolve("refs/heads/master^{tree}");
+ return getLatestDiff(repo, oldTreeId, newTreeId);
+ } finally {
+ rw.release();
+ }
+ } finally {
+ repo.close();
+ }
+ }
+
+ private String getLatestDiff(Repository repo, ObjectId oldTreeId,
+ ObjectId newTreeId) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ DiffFormatter fmt = new DiffFormatter(out);
+ fmt.setRepository(repo);
+ fmt.format(oldTreeId, newTreeId);
+ fmt.flush();
+ return out.toString();
+ }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
new file mode 100644
index 0000000..770e554
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
@@ -0,0 +1,93 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+import static com.google.gerrit.acceptance.git.GitUtil.checkout;
+import static org.junit.Assert.assertEquals;
+
+import com.google.gerrit.acceptance.git.PushOneCommit;
+
+import com.jcraft.jsch.JSchException;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public abstract class AbstractSubmitByMerge extends AbstractSubmit {
+
+ @Test
+ public void submitWithMerge() throws JSchException, IOException,
+ GitAPIException {
+ Git git = createProject();
+ RevCommit initialHead = getRemoteHead();
+ PushOneCommit.Result change =
+ createChange(git, "Change 1", "a.txt", "content");
+ submit(change.getChangeId());
+
+ RevCommit oldHead = getRemoteHead();
+ checkout(git, initialHead.getId().getName());
+ PushOneCommit.Result change2 =
+ createChange(git, "Change 2", "b.txt", "other content");
+ submit(change2.getChangeId());
+ RevCommit head = getRemoteHead();
+ assertEquals(2, head.getParentCount());
+ assertEquals(oldHead, head.getParent(0));
+ assertEquals(change2.getCommitId(), head.getParent(1));
+ }
+
+ @Test
+ public void submitWithContentMerge() throws JSchException, IOException,
+ GitAPIException {
+ Git git = createProject();
+ setUseContentMerge();
+ PushOneCommit.Result change =
+ createChange(git, "Change 1", "a.txt", "aaa\nbbb\nccc\n");
+ submit(change.getChangeId());
+ PushOneCommit.Result change2 =
+ createChange(git, "Change 2", "a.txt", "aaa\nbbb\nccc\nddd\n");
+ submit(change2.getChangeId());
+
+ RevCommit oldHead = getRemoteHead();
+ checkout(git, change.getCommitId().getName());
+ PushOneCommit.Result change3 =
+ createChange(git, "Change 3", "a.txt", "bbb\nccc\n");
+ submit(change3.getChangeId());
+ RevCommit head = getRemoteHead();
+ assertEquals(2, head.getParentCount());
+ assertEquals(oldHead, head.getParent(0));
+ assertEquals(change3.getCommitId(), head.getParent(1));
+ }
+
+ @Test
+ public void submitWithContentMerge_Conflict() throws JSchException,
+ IOException, GitAPIException {
+ Git git = createProject();
+ setUseContentMerge();
+ RevCommit initialHead = getRemoteHead();
+ PushOneCommit.Result change =
+ createChange(git, "Change 1", "a.txt", "content");
+ submit(change.getChangeId());
+
+ RevCommit oldHead = getRemoteHead();
+ checkout(git, initialHead.getId().getName());
+ PushOneCommit.Result change2 =
+ createChange(git, "Change 2", "a.txt", "other content");
+ submitWithConflict(change2.getChangeId());
+ assertEquals(oldHead, getRemoteHead());
+ }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AccountInfo.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AccountInfo.java
new file mode 100644
index 0000000..ee94476
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AccountInfo.java
@@ -0,0 +1,21 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+public class AccountInfo {
+ public Integer _account_id;
+ public String name;
+ public String email;
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUCK b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUCK
index dff94ce..af30705 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUCK
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUCK
@@ -1,14 +1,40 @@
include_defs('//gerrit-acceptance-tests/tests.defs')
acceptance_tests(
- srcs = glob(['*IT.java']),
+ srcs = ['ChangeMessagesIT.java'],
deps = [
':util',
'//gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git:util',
],
)
+acceptance_tests(
+ srcs = ['SubmitByCherryPickIT.java', 'SubmitByFastForwardIT.java',
+ 'SubmitByMergeAlwaysIT.java', 'SubmitByMergeIfNecessaryIT.java',
+ 'SubmitByRebaseIfNecessaryIT.java'],
+ deps = [
+ ':submit',
+ '//gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git:util',
+ ],
+)
+
+java_library(
+ name = 'submit',
+ srcs = ['AbstractSubmit.java', 'AbstractSubmitByMerge.java'],
+ deps = [
+ ':util',
+ '//gerrit-acceptance-tests:lib',
+ '//gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git:util',
+ ],
+)
+
java_library(
name = 'util',
- srcs = ['ChangeInfo.java', 'ChangeMessageInfo.java'],
+ srcs = ['AccountInfo.java', 'ChangeInfo.java', 'ChangeMessageInfo.java',
+ 'GroupInfo.java', 'ProjectConfigInput.java', 'ReviewInput.java',
+ 'SubmitInput.java', 'SuggestReviewerInfo.java'],
+ deps = [
+ '//lib:guava',
+ '//gerrit-reviewdb:server',
+ ],
)
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeInfo.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeInfo.java
index 4c9325e..3921028 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeInfo.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeInfo.java
@@ -18,4 +18,5 @@
public class ChangeInfo {
List<ChangeMessageInfo> messages;
+ String status;
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/GroupInfo.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/GroupInfo.java
new file mode 100644
index 0000000..2c0efff
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/GroupInfo.java
@@ -0,0 +1,20 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+public class GroupInfo {
+ public String id;
+ public String name;
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ProjectConfigInput.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ProjectConfigInput.java
new file mode 100644
index 0000000..4d2e4b6
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ProjectConfigInput.java
@@ -0,0 +1,23 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+import com.google.gerrit.reviewdb.client.Project.InheritableBoolean;
+import com.google.gerrit.reviewdb.client.Project.SubmitType;
+
+public class ProjectConfigInput {
+ public SubmitType submit_type;
+ public InheritableBoolean use_content_merge;
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
new file mode 100644
index 0000000..a5371d2
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ReviewInput.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+import com.google.common.collect.Maps;
+
+import java.util.Map;
+
+public class ReviewInput {
+ Map<String, Integer> labels;
+
+ public static ReviewInput approve() {
+ ReviewInput in = new ReviewInput();
+ in.labels = Maps.newHashMap();
+ in.labels.put("Code-Review", 2);
+ return in;
+ }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
new file mode 100644
index 0000000..ccbbfb6
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
@@ -0,0 +1,143 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+import static com.google.gerrit.acceptance.git.GitUtil.checkout;
+import static org.junit.Assert.assertEquals;
+
+import com.google.gerrit.acceptance.git.PushOneCommit;
+import com.google.gerrit.reviewdb.client.Project.SubmitType;
+
+import com.jcraft.jsch.JSchException;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class SubmitByCherryPickIT extends AbstractSubmit {
+
+ @Override
+ protected SubmitType getSubmitType() {
+ return SubmitType.CHERRY_PICK;
+ }
+
+ @Test
+ public void submitWithCherryPickIfFastForwardPossible() throws JSchException,
+ IOException, GitAPIException {
+ Git git = createProject();
+ PushOneCommit.Result change = createChange(git);
+ submit(change.getChangeId());
+ assertCherryPick(git, false);
+ assertEquals(change.getCommit().getParent(0),
+ getRemoteHead().getParent(0));
+ }
+
+ @Test
+ public void submitWithCherryPick() throws JSchException, IOException,
+ GitAPIException {
+ Git git = createProject();
+ RevCommit initialHead = getRemoteHead();
+ PushOneCommit.Result change =
+ createChange(git, "Change 1", "a.txt", "content");
+ submit(change.getChangeId());
+
+ RevCommit oldHead = getRemoteHead();
+ checkout(git, initialHead.getId().getName());
+ PushOneCommit.Result change2 =
+ createChange(git, "Change 2", "b.txt", "other content");
+ submit(change2.getChangeId());
+ assertCherryPick(git, false);
+ assertEquals(oldHead, getRemoteHead().getParent(0));
+ }
+
+ @Test
+ public void submitWithContentMerge() throws JSchException, IOException,
+ GitAPIException {
+ Git git = createProject();
+ setUseContentMerge();
+ PushOneCommit.Result change =
+ createChange(git, "Change 1", "a.txt", "aaa\nbbb\nccc\n");
+ submit(change.getChangeId());
+ PushOneCommit.Result change2 =
+ createChange(git, "Change 2", "a.txt", "aaa\nbbb\nccc\nddd\n");
+ submit(change2.getChangeId());
+
+ RevCommit oldHead = getRemoteHead();
+ checkout(git, change.getCommitId().getName());
+ PushOneCommit.Result change3 =
+ createChange(git, "Change 3", "a.txt", "bbb\nccc\n");
+ submit(change3.getChangeId());
+ assertCherryPick(git, true);
+ assertEquals(oldHead, getRemoteHead().getParent(0));
+ }
+
+ @Test
+ public void submitWithContentMerge_Conflict() throws JSchException,
+ IOException, GitAPIException {
+ Git git = createProject();
+ setUseContentMerge();
+ RevCommit initialHead = getRemoteHead();
+ PushOneCommit.Result change =
+ createChange(git, "Change 1", "a.txt", "content");
+ submit(change.getChangeId());
+
+ RevCommit oldHead = getRemoteHead();
+ checkout(git, initialHead.getId().getName());
+ PushOneCommit.Result change2 =
+ createChange(git, "Change 2", "a.txt", "other content");
+ submitWithConflict(change2.getChangeId());
+ assertEquals(oldHead, getRemoteHead());
+ }
+
+ @Test
+ public void submitOutOfOrder() throws JSchException, IOException,
+ GitAPIException {
+ Git git = createProject();
+ RevCommit initialHead = getRemoteHead();
+ PushOneCommit.Result change =
+ createChange(git, "Change 1", "a.txt", "content");
+ submit(change.getChangeId());
+
+ RevCommit oldHead = getRemoteHead();
+ checkout(git, initialHead.getId().getName());
+ createChange(git, "Change 2", "b.txt", "other content");
+ PushOneCommit.Result change3 =
+ createChange(git, "Change 3", "c.txt", "different content");
+ submit(change3.getChangeId());
+ assertCherryPick(git, false);
+ assertEquals(oldHead, getRemoteHead().getParent(0));
+ }
+
+ @Test
+ public void submitOutOfOrder_Conflict() throws JSchException, IOException,
+ GitAPIException {
+ Git git = createProject();
+ RevCommit initialHead = getRemoteHead();
+ PushOneCommit.Result change =
+ createChange(git, "Change 1", "a.txt", "content");
+ submit(change.getChangeId());
+
+ RevCommit oldHead = getRemoteHead();
+ checkout(git, initialHead.getId().getName());
+ createChange(git, "Change 2", "b.txt", "other content");
+ PushOneCommit.Result change3 =
+ createChange(git, "Change 3", "b.txt", "different content");
+ submitWithConflict(change3.getChangeId());
+ assertEquals(oldHead, getRemoteHead());
+ }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
new file mode 100644
index 0000000..9d56e18
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+import static com.google.gerrit.acceptance.git.GitUtil.checkout;
+import static org.junit.Assert.assertEquals;
+
+import com.google.gerrit.acceptance.git.PushOneCommit;
+import com.google.gerrit.reviewdb.client.Project.SubmitType;
+
+import com.jcraft.jsch.JSchException;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class SubmitByFastForwardIT extends AbstractSubmit {
+
+ @Override
+ protected SubmitType getSubmitType() {
+ return SubmitType.FAST_FORWARD_ONLY;
+ }
+
+ @Test
+ public void submitWithFastForward() throws JSchException, IOException,
+ GitAPIException {
+ Git git = createProject();
+ RevCommit oldHead = getRemoteHead();
+ PushOneCommit.Result change = createChange(git);
+ submit(change.getChangeId());
+ RevCommit head = getRemoteHead();
+ assertEquals(change.getCommitId(), head.getId());
+ assertEquals(oldHead, head.getParent(0));
+ }
+
+ @Test
+ public void submitFastForwardNotPossible_Conflict() throws JSchException, IOException,
+ GitAPIException {
+ Git git = createProject();
+ RevCommit initialHead = getRemoteHead();
+ PushOneCommit.Result change =
+ createChange(git, "Change 1", "a.txt", "content");
+ submit(change.getChangeId());
+
+ RevCommit oldHead = getRemoteHead();
+ checkout(git, initialHead.getId().getName());
+ PushOneCommit.Result change2 =
+ createChange(git, "Change 2", "b.txt", "other content");
+ submitWithConflict(change2.getChangeId());
+ assertEquals(oldHead, getRemoteHead());
+ }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java
new file mode 100644
index 0000000..6c671eb
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.gerrit.acceptance.git.PushOneCommit;
+import com.google.gerrit.reviewdb.client.Project.SubmitType;
+
+import com.jcraft.jsch.JSchException;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class SubmitByMergeAlwaysIT extends AbstractSubmitByMerge {
+
+ @Override
+ protected SubmitType getSubmitType() {
+ return SubmitType.MERGE_ALWAYS;
+ }
+
+ @Test
+ public void submitWithMergeIfFastForwardPossible() throws JSchException,
+ IOException, GitAPIException {
+ Git git = createProject();
+ RevCommit oldHead = getRemoteHead();
+ PushOneCommit.Result change = createChange(git);
+ submit(change.getChangeId());
+ RevCommit head = getRemoteHead();
+ assertEquals(2, head.getParentCount());
+ assertEquals(oldHead, head.getParent(0));
+ assertEquals(change.getCommitId(), head.getParent(1));
+ }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
new file mode 100644
index 0000000..a5737a7
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
@@ -0,0 +1,35 @@
+package com.google.gerrit.acceptance.rest.change;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.gerrit.acceptance.git.PushOneCommit;
+import com.google.gerrit.reviewdb.client.Project.SubmitType;
+
+import com.jcraft.jsch.JSchException;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
+
+ @Override
+ protected SubmitType getSubmitType() {
+ return SubmitType.MERGE_IF_NECESSARY;
+ }
+
+ @Test
+ public void submitWithFastForward() throws JSchException, IOException,
+ GitAPIException {
+ Git git = createProject();
+ RevCommit oldHead = getRemoteHead();
+ PushOneCommit.Result change = createChange(git);
+ submit(change.getChangeId());
+ RevCommit head = getRemoteHead();
+ assertEquals(change.getCommitId(), head.getId());
+ assertEquals(oldHead, head.getParent(0));
+ }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java
new file mode 100644
index 0000000..07594a6
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java
@@ -0,0 +1,108 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+import static com.google.gerrit.acceptance.git.GitUtil.checkout;
+import static org.junit.Assert.assertEquals;
+
+import com.google.gerrit.acceptance.git.PushOneCommit;
+import com.google.gerrit.reviewdb.client.Project.SubmitType;
+
+import com.jcraft.jsch.JSchException;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class SubmitByRebaseIfNecessaryIT extends AbstractSubmit {
+
+ @Override
+ protected SubmitType getSubmitType() {
+ return SubmitType.REBASE_IF_NECESSARY;
+ }
+
+ @Test
+ public void submitWithFastForward() throws JSchException, IOException,
+ GitAPIException {
+ Git git = createProject();
+ RevCommit oldHead = getRemoteHead();
+ PushOneCommit.Result change = createChange(git);
+ submit(change.getChangeId());
+ RevCommit head = getRemoteHead();
+ assertEquals(change.getCommitId(), head.getId());
+ assertEquals(oldHead, head.getParent(0));
+ }
+
+ @Test
+ public void submitWithRebase() throws JSchException, IOException,
+ GitAPIException {
+ Git git = createProject();
+ RevCommit initialHead = getRemoteHead();
+ PushOneCommit.Result change =
+ createChange(git, "Change 1", "a.txt", "content");
+ submit(change.getChangeId());
+
+ RevCommit oldHead = getRemoteHead();
+ checkout(git, initialHead.getId().getName());
+ PushOneCommit.Result change2 =
+ createChange(git, "Change 2", "b.txt", "other content");
+ submit(change2.getChangeId());
+ assertRebase(git, false);
+ assertEquals(oldHead, getRemoteHead().getParent(0));
+ }
+
+ @Test
+ public void submitWithContentMerge() throws JSchException, IOException,
+ GitAPIException {
+ Git git = createProject();
+ setUseContentMerge();
+ PushOneCommit.Result change =
+ createChange(git, "Change 1", "a.txt", "aaa\nbbb\nccc\n");
+ submit(change.getChangeId());
+ PushOneCommit.Result change2 =
+ createChange(git, "Change 2", "a.txt", "aaa\nbbb\nccc\nddd\n");
+ submit(change2.getChangeId());
+
+ RevCommit oldHead = getRemoteHead();
+ checkout(git, change.getCommitId().getName());
+ PushOneCommit.Result change3 =
+ createChange(git, "Change 3", "a.txt", "bbb\nccc\n");
+ submit(change3.getChangeId());
+ assertRebase(git, true);
+ assertEquals(oldHead, getRemoteHead().getParent(0));
+ }
+
+ @Test
+ public void submitWithContentMerge_Conflict() throws JSchException,
+ IOException, GitAPIException {
+ Git git = createProject();
+ setUseContentMerge();
+ RevCommit initialHead = getRemoteHead();
+ PushOneCommit.Result change =
+ createChange(git, "Change 1", "a.txt", "content");
+ submit(change.getChangeId());
+
+ RevCommit oldHead = getRemoteHead();
+ checkout(git, initialHead.getId().getName());
+ PushOneCommit.Result change2 =
+ createChange(git, "Change 2", "a.txt", "other content");
+ submitWithConflict(change2.getChangeId());
+ RevCommit head = getRemoteHead();
+ assertEquals(oldHead, head);
+ }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitInput.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitInput.java
new file mode 100644
index 0000000..8e1b340
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitInput.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+public class SubmitInput {
+ boolean wait_for_merge;
+
+ public static SubmitInput waitForMerge() {
+ SubmitInput in = new SubmitInput();
+ in.wait_for_merge = true;
+ return in;
+ }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewerInfo.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewerInfo.java
new file mode 100644
index 0000000..212ee86
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewerInfo.java
@@ -0,0 +1,20 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+public class SuggestReviewerInfo {
+ public AccountInfo account;
+ public GroupInfo group;
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
new file mode 100644
index 0000000..1ed4d61
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
@@ -0,0 +1,154 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+import static com.google.gerrit.acceptance.git.GitUtil.cloneProject;
+import static com.google.gerrit.acceptance.git.GitUtil.createProject;
+import static com.google.gerrit.acceptance.git.GitUtil.initSsh;
+import static org.junit.Assert.assertEquals;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.AccountCreator;
+import com.google.gerrit.acceptance.GerritConfig;
+import com.google.gerrit.acceptance.GerritConfigs;
+import com.google.gerrit.acceptance.RestSession;
+import com.google.gerrit.acceptance.SshSession;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.git.PushOneCommit;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.google.gwtorm.server.SchemaFactory;
+import com.google.inject.Inject;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.List;
+
+public class SuggestReviewersIT extends AbstractDaemonTest {
+
+ @Inject
+ private AccountCreator accounts;
+
+ @Inject
+ private SchemaFactory<ReviewDb> reviewDbProvider;
+
+ private TestAccount admin;
+ private RestSession session;
+ private Git git;
+ private ReviewDb db;
+ private Project.NameKey project;
+
+ @Before
+ public void setUp() throws Exception {
+ admin = accounts.admin();
+ session = new RestSession(server, admin);
+
+ group("users1");
+ group("users2");
+ group("users3");
+
+ accounts.create("user1", "user1@example.com", "User1", "users1");
+ accounts.create("user2", "user2@example.com", "User2", "users2");
+ accounts.create("user3", "user3@example.com", "User3", "users1", "users2");
+
+ initSsh(admin);
+ project = new Project.NameKey("p");
+ SshSession sshSession = new SshSession(server, admin);
+ createProject(sshSession, project.get());
+ git = cloneProject(sshSession.getUrl() + "/" + project.get());
+ sshSession.close();
+ db = reviewDbProvider.open();
+ }
+
+ @After
+ public void cleanup() {
+ db.close();
+ }
+
+ @Test
+ @GerritConfig(name = "suggest.accounts", value = "false")
+ public void suggestReviewersNoResult1() throws GitAPIException, IOException,
+ Exception {
+ String changeId = createChange(admin);
+ List<SuggestReviewerInfo> reviewers = suggestReviewers(changeId, "u", 6);
+ assertEquals(reviewers.size(), 0);
+ }
+
+ @Test
+ @GerritConfigs(
+ {@GerritConfig(name = "suggest.accounts", value = "true"),
+ @GerritConfig(name = "suggest.from", value = "1"),
+ @GerritConfig(name = "accounts.visibility", value = "NONE"),
+ })
+ public void suggestReviewersNoResult2() throws GitAPIException, IOException,
+ Exception {
+ String changeId = createChange(admin);
+ List<SuggestReviewerInfo> reviewers = suggestReviewers(changeId, "u", 6);
+ assertEquals(reviewers.size(), 0);
+ }
+
+ @Test
+ @GerritConfig(name = "suggest.from", value = "2")
+ public void suggestReviewersNoResult3() throws GitAPIException, IOException,
+ Exception {
+ String changeId = createChange(admin);
+ List<SuggestReviewerInfo> reviewers = suggestReviewers(changeId, "u", 6);
+ assertEquals(reviewers.size(), 0);
+ }
+
+ @Test
+ public void suggestReviewersChange() throws GitAPIException,
+ IOException, Exception {
+ String changeId = createChange(admin);
+ List<SuggestReviewerInfo> reviewers = suggestReviewers(changeId, "u", 6);
+ assertEquals(reviewers.size(), 6);
+ reviewers = suggestReviewers(changeId, "u", 5);
+ assertEquals(reviewers.size(), 5);
+ reviewers = suggestReviewers(changeId, "users3", 10);
+ assertEquals(reviewers.size(), 1);
+ }
+
+ private List<SuggestReviewerInfo> suggestReviewers(String changeId,
+ String query, int n)
+ throws IOException {
+ return new Gson().fromJson(
+ session.get("/changes/"
+ + changeId
+ + "/suggest_reviewers?q="
+ + query
+ + "&n="
+ + n)
+ .getReader(),
+ new TypeToken<List<SuggestReviewerInfo>>() {}
+ .getType());
+ }
+
+ private void group(String name) throws IOException {
+ session.put("/groups/" + name, new Object()).consume();
+ }
+
+ private String createChange(TestAccount account) throws GitAPIException,
+ IOException {
+ PushOneCommit push = new PushOneCommit(db, account.getIdent());
+ return push.to(git, "refs/for/master").getChangeId();
+ }
+}
\ No newline at end of file
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
index ad415d4..05425a5 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
@@ -54,12 +54,8 @@
return "/c/" + c + "/";
}
- public static String toChange2(final Change.Id c) {
- return "/c2/" + c + "/";
- }
-
- public static String toChange2(Change.Id c, String p) {
- return "/c2/" + c + "/" + p;
+ public static String toChange(Change.Id c, String p) {
+ return "/c/" + c + "/" + p;
}
public static String toChange(final PatchSet.Id ps) {
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
index f85e333..c4c388f 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
@@ -16,6 +16,7 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Account.FieldName;
+import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadScheme;
import com.google.gerrit.reviewdb.client.AuthType;
@@ -51,6 +52,7 @@
protected String anonymousCowardName;
protected int suggestFrom;
protected int changeUpdateDelay;
+ protected AccountGeneralPreferences.ChangeScreen changeScreen;
public String getLoginUrl() {
return loginUrl;
@@ -270,4 +272,12 @@
public void setChangeUpdateDelay(int seconds) {
changeUpdateDelay = seconds;
}
+
+ public AccountGeneralPreferences.ChangeScreen getChangeScreen() {
+ return changeScreen;
+ }
+
+ public void setChangeScreen(AccountGeneralPreferences.ChangeScreen ui) {
+ this.changeScreen = ui;
+ }
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
index 685c4b8..3c4e334 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
@@ -81,6 +81,7 @@
import com.google.gerrit.common.data.PatchSetDetail;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DiffView;
+import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Patch;
@@ -93,8 +94,6 @@
import com.google.gwtorm.client.KeyUtil;
public class Dispatcher {
- private static boolean useChangeScreen2;
-
public static String toPatchSideBySide(final Patch.Key id) {
return toPatch("", null, id);
}
@@ -203,9 +202,6 @@
} else if (matchPrefix("/c/", token)) {
change(token);
- } else if (matchPrefix("/c2/", token)) {
- change2(token);
-
} else if (matchExact(MINE, token)) {
Gerrit.display(token, mine(token));
@@ -480,7 +476,7 @@
if (rest.isEmpty()) {
Gerrit.display(token, panel== null
- ? (useChangeScreen2
+ ? (isChangeScreen2()
? new ChangeScreen2(id, null, false)
: new ChangeScreen(id))
: new NotFoundScreen());
@@ -513,7 +509,7 @@
patch(token, base, p, 0, null, null, panel);
} else {
if (panel == null) {
- Gerrit.display(token, useChangeScreen2
+ Gerrit.display(token, isChangeScreen2()
? new ChangeScreen2(id, String.valueOf(ps.get()), false)
: new ChangeScreen(id));
} else if ("publish".equals(panel)) {
@@ -524,19 +520,17 @@
}
}
- private static void change2(final String token) {
- String rest = skip(token);
- Change.Id id;
- int s = rest.indexOf('/');
- if (0 <= s) {
- id = Change.Id.parse(rest.substring(0, s));
- rest = rest.substring(s + 1);
- } else {
- id = Change.Id.parse(rest);
- rest = "";
+ private static boolean isChangeScreen2() {
+ AccountGeneralPreferences.ChangeScreen ui = null;
+ if (Gerrit.isSignedIn()) {
+ ui = Gerrit.getUserAccount()
+ .getGeneralPreferences()
+ .getChangeScreen();
}
- useChangeScreen2 = true;
- Gerrit.display(token, new ChangeScreen2(id, rest, false));
+ if (ui == null) {
+ ui = Gerrit.getConfig().getChangeScreen();
+ }
+ return ui == AccountGeneralPreferences.ChangeScreen.CHANGE_SCREEN2;
}
private static void publish(final PatchSet.Id ps) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
index fb72085..717bf8c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
@@ -48,6 +48,8 @@
String inactiveAccountBody();
+ String labelNotApplicable();
+
String menuAll();
String menuAllOpen();
String menuAllMerged();
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 6f3dca5..4c4fcf7 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
@@ -31,6 +31,8 @@
inactiveAccountBody = This user is currently inactive.
+labelNotApplicable = Label not applicable
+
menuAll = All
menuAllOpen = Open
menuAllMerged = Merged
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
index 2fef7ee..b285c0b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
@@ -141,6 +141,7 @@
String infoTable();
String inputFieldTypeHint();
String labelList();
+ String labelNotApplicable();
String leftMostCell();
String lineHeader();
String lineNumber();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
index 28a96149..e0e2d22 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
@@ -26,6 +26,7 @@
String accountId();
String commentVisibilityLabel();
+ String changeScreenLabel();
String diffViewLabel();
String maximumPageSizeFieldLabel();
String dateFormatLabel();
@@ -38,6 +39,9 @@
String buttonSaveChanges();
String showRelativeDateInChangeTable();
+ String changeScreenOldUi();
+ String changeScreenNewUi();
+
String tabAccountSummary();
String tabPreferences();
String tabWatchedProjects();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
index ab7e5a9..fde1c2e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
@@ -12,12 +12,16 @@
showUsernameInReviewCategory = Display Person Name In Review Category
maximumPageSizeFieldLabel = Maximum Page Size:
commentVisibilityLabel = Comment Visibility:
-diffViewLabel = Diff View (ChangeScreen2 only):
+changeScreenLabel = Change View:
+diffViewLabel = Diff View (Change Screen 2):
dateFormatLabel = Date/Time Format:
contextWholeFile = Whole File
buttonSaveChanges = Save Changes
showRelativeDateInChangeTable = Show Relative Dates in Changes Table
+changeScreenOldUi = Old Screen
+changeScreenNewUi = Change Screen 2
+
tabAccountSummary = Profile
tabPreferences = Preferences
tabWatchedProjects = Watched Projects
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java
index 2014f19..e55be79 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java
@@ -21,6 +21,7 @@
public interface AccountMessages extends Messages {
String lines(short cnt);
String rowsPerPage(short cnt);
+ String changeScreenServerDefault(String d);
String enterIAGREE(String iagree);
String contactOnFile(Date lastDate);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties
index d013911..313893e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties
@@ -1,5 +1,7 @@
lines = {0} lines
rowsPerPage = {0} rows per page
+changeScreenServerDefault = Server Default ({0})
+
enterIAGREE = (enter {0} in the box to the left)
contactOnFile = Contact information last updated on {0,date,medium} at {0,time,short}.
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
index dbc8480..115a46f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
@@ -48,6 +48,7 @@
private ListBox dateFormat;
private ListBox timeFormat;
private ListBox commentVisibilityStrategy;
+ private ListBox changeScreen;
private ListBox diffView;
private Button save;
@@ -68,30 +69,36 @@
commentVisibilityStrategy = new ListBox();
commentVisibilityStrategy.addItem(
com.google.gerrit.client.changes.Util.C.messageCollapseAll(),
- AccountGeneralPreferences.CommentVisibilityStrategy.COLLAPSE_ALL.name()
- );
+ AccountGeneralPreferences.CommentVisibilityStrategy.COLLAPSE_ALL.name());
commentVisibilityStrategy.addItem(
com.google.gerrit.client.changes.Util.C.messageExpandMostRecent(),
- AccountGeneralPreferences.CommentVisibilityStrategy.EXPAND_MOST_RECENT.name()
- );
+ AccountGeneralPreferences.CommentVisibilityStrategy.EXPAND_MOST_RECENT.name());
commentVisibilityStrategy.addItem(
com.google.gerrit.client.changes.Util.C.messageExpandRecent(),
- AccountGeneralPreferences.CommentVisibilityStrategy.EXPAND_RECENT.name()
- );
+ AccountGeneralPreferences.CommentVisibilityStrategy.EXPAND_RECENT.name());
commentVisibilityStrategy.addItem(
com.google.gerrit.client.changes.Util.C.messageExpandAll(),
- AccountGeneralPreferences.CommentVisibilityStrategy.EXPAND_ALL.name()
- );
+ AccountGeneralPreferences.CommentVisibilityStrategy.EXPAND_ALL.name());
+
+ changeScreen = new ListBox();
+ changeScreen.addItem(
+ Util.M.changeScreenServerDefault(
+ getLabel(Gerrit.getConfig().getChangeScreen())),
+ "");
+ changeScreen.addItem(
+ Util.C.changeScreenOldUi(),
+ AccountGeneralPreferences.ChangeScreen.OLD_UI.name());
+ changeScreen.addItem(
+ Util.C.changeScreenNewUi(),
+ AccountGeneralPreferences.ChangeScreen.CHANGE_SCREEN2.name());
diffView = new ListBox();
diffView.addItem(
com.google.gerrit.client.changes.Util.C.sideBySide(),
- AccountGeneralPreferences.DiffView.SIDE_BY_SIDE.name()
- );
+ AccountGeneralPreferences.DiffView.SIDE_BY_SIDE.name());
diffView.addItem(
com.google.gerrit.client.changes.Util.C.unifiedDiff(),
- AccountGeneralPreferences.DiffView.UNIFIED_DIFF.name()
- );
+ AccountGeneralPreferences.DiffView.UNIFIED_DIFF.name());
Date now = new Date();
dateFormat = new ListBox();
@@ -129,7 +136,7 @@
relativeDateInChangeTable = new CheckBox(Util.C.showRelativeDateInChangeTable());
- final Grid formGrid = new Grid(10, 2);
+ final Grid formGrid = new Grid(11, 2);
int row = 0;
formGrid.setText(row, labelIdx, "");
@@ -168,6 +175,10 @@
formGrid.setWidget(row, fieldIdx, commentVisibilityStrategy);
row++;
+ formGrid.setText(row, labelIdx, Util.C.changeScreenLabel());
+ formGrid.setWidget(row, fieldIdx, changeScreen);
+ row++;
+
formGrid.setText(row, labelIdx, Util.C.diffViewLabel());
formGrid.setWidget(row, fieldIdx, diffView);
row++;
@@ -195,6 +206,7 @@
e.listenTo(timeFormat);
e.listenTo(relativeDateInChangeTable);
e.listenTo(commentVisibilityStrategy);
+ e.listenTo(changeScreen);
e.listenTo(diffView);
}
@@ -219,6 +231,7 @@
timeFormat.setEnabled(on);
relativeDateInChangeTable.setEnabled(on);
commentVisibilityStrategy.setEnabled(on);
+ changeScreen.setEnabled(on);
diffView.setEnabled(on);
}
@@ -237,6 +250,9 @@
setListBox(commentVisibilityStrategy,
AccountGeneralPreferences.CommentVisibilityStrategy.EXPAND_RECENT,
p.getCommentVisibilityStrategy());
+ setListBox(changeScreen,
+ null,
+ p.getChangeScreen());
setListBox(diffView,
AccountGeneralPreferences.DiffView.SIDE_BY_SIDE,
p.getDiffView());
@@ -249,7 +265,8 @@
private <T extends Enum<?>> void setListBox(final ListBox f,
final T defaultValue, final T currentValue) {
- setListBox(f, defaultValue.name(), //
+ setListBox(f,
+ defaultValue != null ? defaultValue.name() : "",
currentValue != null ? currentValue.name() : "");
}
@@ -280,6 +297,9 @@
final int idx = f.getSelectedIndex();
if (0 <= idx) {
String v = f.getValue(idx);
+ if ("".equals(v)) {
+ return defaultValue;
+ }
for (T t : all) {
if (t.name().equals(v)) {
return t;
@@ -310,6 +330,9 @@
p.setDiffView(getListBox(diffView,
AccountGeneralPreferences.DiffView.SIDE_BY_SIDE,
AccountGeneralPreferences.DiffView.values()));
+ p.setChangeScreen(getListBox(changeScreen,
+ null,
+ AccountGeneralPreferences.ChangeScreen.values()));
enable(false);
save.setEnabled(false);
@@ -330,4 +353,18 @@
}
});
}
+
+ private static String getLabel(AccountGeneralPreferences.ChangeScreen ui) {
+ if (ui == null) {
+ return "";
+ }
+ switch (ui) {
+ case OLD_UI:
+ return Util.C.changeScreenOldUi();
+ case CHANGE_SCREEN2:
+ return Util.C.changeScreenNewUi();
+ default:
+ return ui.name();
+ }
+ }
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java
index 4faed50..77724f8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java
@@ -38,7 +38,7 @@
Window.alert(str.asString());
}
}
- Gerrit.display(PageLinks.toChange2(id));
+ Gerrit.display(PageLinks.toChange(id));
}
};
if ("PUT".equalsIgnoreCase(action.method())) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AbandonAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AbandonAction.java
index d46edd3..60fbee4 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AbandonAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AbandonAction.java
@@ -34,7 +34,7 @@
ChangeApi.abandon(id.get(), message, new GerritCallback<ChangeInfo>() {
@Override
public void onSuccess(ChangeInfo result) {
- Gerrit.display(PageLinks.toChange2(id));
+ Gerrit.display(PageLinks.toChange(id));
hide();
}
});
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java
index 09aef22..c6940de 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java
@@ -120,7 +120,7 @@
@UiField AnchorElement permalink;
@UiField Element reviewersText;
- @UiField Element ccText;
+ @UiField Reviewers reviewers;
@UiField Element changeIdText;
@UiField Element ownerText;
@UiField Element statusText;
@@ -208,6 +208,7 @@
Resources.I.style().ensureInjected();
star.setVisible(Gerrit.isSignedIn());
labels.init(style, statusText);
+ reviewers.init(style);
keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation());
keysNavigation.add(new KeyCommand(0, 'u', Util.C.upToChangeList()) {
@@ -241,6 +242,12 @@
star.setValue(!star.getValue(), true);
}
});
+ keysAction.add(new KeyCommand(0, 'c', Util.C.keyAddReviewers()) {
+ @Override
+ public void onKeyPress(KeyPressEvent event) {
+ reviewers.onOpenForm();
+ }
+ });
}
}
@@ -654,8 +661,9 @@
}
r.remove(info.owner()._account_id());
cc.remove(info.owner()._account_id());
- reviewersText.setInnerSafeHtml(labels.formatUserList(r.values()));
- ccText.setInnerSafeHtml(labels.formatUserList(cc.values()));
+ reviewersText.setInnerSafeHtml(Labels.formatUserList(style, r.values()));
+ reviewers.set(info.legacy_id());
+ reviewers.setReviewers(Labels.formatUserList(style, cc.values()));
}
private void renderOwner(ChangeInfo info) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml
index 6cf7b7b..2c47c4d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml
@@ -281,7 +281,9 @@
</tr>
<tr>
<th><ui:msg>CC</ui:msg></th>
- <td ui:field='ccText'/>
+ <td>
+ <c:Reviewers ui:field='reviewers'/>
+ </td>
</tr>
<tr>
<th><ui:msg>Project</ui:msg></th>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java
index 2c0db0b..0dc155e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java
@@ -51,7 +51,7 @@
public void onSuccess(ChangeInfo result) {
sent = true;
hide();
- Gerrit.display(PageLinks.toChange2(result.legacy_id()));
+ Gerrit.display(PageLinks.toChange(result.legacy_id()));
}
@Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.java
index 5ddd21b..cd1f304 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.java
@@ -80,7 +80,7 @@
new GerritCallback<JavaScriptObject>() {
@Override
public void onSuccess(JavaScriptObject msg) {
- Gerrit.display(PageLinks.toChange2(changeId));
+ Gerrit.display(PageLinks.toChange(changeId));
hide();
};
});
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
index f3ce7fc..6316c64 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
@@ -123,7 +123,7 @@
html.setStyleName(style.label_reject());
}
html.append(val).append(" ");
- html.append(formatUserList(m.get(v)));
+ html.append(formatUserList(style, m.get(v)));
html.closeSpan();
}
return html.toBlockWidget();
@@ -167,7 +167,8 @@
}
}
- SafeHtml formatUserList(Collection<? extends AccountInfo> in) {
+ static SafeHtml formatUserList(ChangeScreen2.Style style,
+ Collection<? extends AccountInfo> in) {
List<AccountInfo> users = new ArrayList<AccountInfo>(in);
Collections.sort(users, new Comparator<AccountInfo>() {
@Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java
index 7836de5..ddb7d56 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java
@@ -112,7 +112,7 @@
.post(input, new GerritCallback<ReviewInput>() {
@Override
public void onSuccess(ReviewInput result) {
- Gerrit.display(PageLinks.toChange2(changeId));
+ Gerrit.display(PageLinks.toChange(changeId));
}
});
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RebaseAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RebaseAction.java
index 0dd6072..2b1c250 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RebaseAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RebaseAction.java
@@ -26,7 +26,7 @@
ChangeApi.rebase(id.get(), revision,
new GerritCallback<ChangeInfo>() {
public void onSuccess(ChangeInfo result) {
- Gerrit.display(PageLinks.toChange2(id));
+ Gerrit.display(PageLinks.toChange(id));
}
});
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java
index cddf1bf..f8a7cc7 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java
@@ -174,7 +174,7 @@
private String url(ChangeAndCommit c) {
if (c.has_change_number() && c.has_revision_number()) {
PatchSet.Id id = c.patch_set_id();
- return "#" + PageLinks.toChange2(
+ return "#" + PageLinks.toChange(
id.getParentKey(),
String.valueOf(id.get()));
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reload.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reload.java
index bd0c0e2..7b88b2b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reload.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reload.java
@@ -47,7 +47,7 @@
}
void reload() {
- Gerrit.display(PageLinks.toChange2(changeId));
+ Gerrit.display(PageLinks.toChange(changeId));
}
@Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
index 01110de..f17c01f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
@@ -144,7 +144,7 @@
.post(in, new GerritCallback<ReviewInput>() {
@Override
public void onSuccess(ReviewInput result) {
- Gerrit.display(PageLinks.toChange2(
+ Gerrit.display(PageLinks.toChange(
psId.getParentKey(),
String.valueOf(psId.get())));
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RestReviewerSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RestReviewerSuggestOracle.java
new file mode 100644
index 0000000..7575869
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RestReviewerSuggestOracle.java
@@ -0,0 +1,89 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.client.change;
+
+import com.google.gerrit.client.FormatUtil;
+import com.google.gerrit.client.account.AccountInfo;
+import com.google.gerrit.client.admin.Util;
+import com.google.gerrit.client.changes.ChangeApi;
+import com.google.gerrit.client.groups.GroupBaseInfo;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.rpc.Natives;
+import com.google.gerrit.client.ui.SuggestAfterTypingNCharsOracle;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.user.client.ui.SuggestOracle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** REST API based suggestion Oracle for reviewers. */
+public class RestReviewerSuggestOracle extends SuggestAfterTypingNCharsOracle {
+
+ private Change.Id changeId;
+
+ @Override
+ protected void _onRequestSuggestions(final Request req, final Callback callback) {
+ ChangeApi.suggestReviewers(changeId.get(), req.getQuery(),
+ req.getLimit()).get(new GerritCallback<JsArray<SuggestReviewerInfo>>() {
+ @Override
+ public void onSuccess(JsArray<SuggestReviewerInfo> result) {
+ final List<RestReviewerSuggestion> r =
+ new ArrayList<RestReviewerSuggestion>(result.length());
+ for (final SuggestReviewerInfo reviewer : Natives.asList(result)) {
+ r.add(new RestReviewerSuggestion(reviewer));
+ }
+ callback.onSuggestionsReady(req, new Response(r));
+ }
+ });
+ }
+
+ public void setChange(Change.Id changeId) {
+ this.changeId = changeId;
+ }
+
+ private static class RestReviewerSuggestion implements SuggestOracle.Suggestion {
+ private final SuggestReviewerInfo reviewer;
+
+ RestReviewerSuggestion(final SuggestReviewerInfo reviewer) {
+ this.reviewer = reviewer;
+ }
+
+ public String getDisplayString() {
+ if (reviewer.account() != null) {
+ return FormatUtil.nameEmail(reviewer.account());
+ }
+ return reviewer.group().name()
+ + " ("
+ + Util.C.suggestedGroupLabel()
+ + ")";
+ }
+
+ public String getReplacementString() {
+ if (reviewer.account() != null) {
+ return FormatUtil.nameEmail(reviewer.account());
+ }
+ return reviewer.group().name();
+ }
+ }
+
+ public static class SuggestReviewerInfo extends JavaScriptObject {
+ public final native AccountInfo account() /*-{ return this.account; }-*/;
+ public final native GroupBaseInfo group() /*-{ return this.group; }-*/;
+ protected SuggestReviewerInfo() {
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RestoreAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RestoreAction.java
index 82f9b0c..215f39d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RestoreAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RestoreAction.java
@@ -34,7 +34,7 @@
ChangeApi.restore(id.get(), message, new GerritCallback<ChangeInfo>() {
@Override
public void onSuccess(ChangeInfo result) {
- Gerrit.display(PageLinks.toChange2(id));
+ Gerrit.display(PageLinks.toChange(id));
hide();
}
});
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java
index 3190d79..5f9f076 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java
@@ -46,7 +46,7 @@
public void onSuccess(ChangeInfo result) {
sent = true;
hide();
- Gerrit.display(PageLinks.toChange2(result.legacy_id()));
+ Gerrit.display(PageLinks.toChange(result.legacy_id()));
}
@Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java
new file mode 100644
index 0000000..b4e1f55
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java
@@ -0,0 +1,225 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.client.change;
+
+import com.google.gerrit.client.ConfirmationCallback;
+import com.google.gerrit.client.ConfirmationDialog;
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.account.AccountInfo;
+import com.google.gerrit.client.changes.ApprovalTable.PostInput;
+import com.google.gerrit.client.changes.ApprovalTable.PostResult;
+import com.google.gerrit.client.changes.ChangeApi;
+import com.google.gerrit.client.changes.ChangeInfo;
+import com.google.gerrit.client.changes.ChangeInfo.ApprovalInfo;
+import com.google.gerrit.client.changes.ChangeInfo.LabelInfo;
+import com.google.gerrit.client.changes.Util;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.rpc.Natives;
+import com.google.gerrit.client.ui.HintTextBox;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyDownEvent;
+import com.google.gwt.event.dom.client.KeyDownHandler;
+import com.google.gwt.event.logical.shared.SelectionEvent;
+import com.google.gwt.event.logical.shared.SelectionHandler;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.rpc.StatusCodeException;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.HTMLPanel;
+import com.google.gwt.user.client.ui.SuggestBox;
+import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay;
+import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
+import com.google.gwt.user.client.ui.UIObject;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** Add reviewers. */
+class Reviewers extends Composite {
+ interface Binder extends UiBinder<HTMLPanel, Reviewers> {}
+ private static final Binder uiBinder = GWT.create(Binder.class);
+
+ @UiField Button openForm;
+ @UiField Element reviewers;
+ @UiField Element form;
+ @UiField Element error;
+ @UiField(provided = true)
+ SuggestBox suggestBox;
+
+ private RestReviewerSuggestOracle reviewerSuggestOracle;
+ private HintTextBox nameTxtBox;
+ private Change.Id changeId;
+ private ChangeScreen2.Style style;
+ private boolean submitOnSelection;
+
+ Reviewers() {
+ reviewerSuggestOracle = new RestReviewerSuggestOracle();
+ nameTxtBox = new HintTextBox();
+ suggestBox = new SuggestBox(reviewerSuggestOracle, nameTxtBox);
+ initWidget(uiBinder.createAndBindUi(this));
+
+ nameTxtBox.setVisibleLength(55);
+ nameTxtBox.setHintText(Util.C.approvalTableAddReviewerHint());
+ nameTxtBox.addKeyDownHandler(new KeyDownHandler() {
+ @Override
+ public void onKeyDown(KeyDownEvent e) {
+ submitOnSelection = false;
+
+ if (e.getNativeEvent().getKeyCode() == KeyCodes.KEY_ESCAPE) {
+ onCancel(null);
+ } else if (e.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
+ if (((DefaultSuggestionDisplay) suggestBox.getSuggestionDisplay())
+ .isSuggestionListShowing()) {
+ submitOnSelection = true;
+ } else {
+ onAdd(null);
+ }
+ }
+ }
+ });
+ suggestBox.addSelectionHandler(new SelectionHandler<Suggestion>() {
+ @Override
+ public void onSelection(SelectionEvent<Suggestion> event) {
+ nameTxtBox.setFocus(true);
+ if (submitOnSelection) {
+ onAdd(null);
+ }
+ }
+ });
+ }
+
+ void set(Change.Id changeId) {
+ this.changeId = changeId;
+ reviewerSuggestOracle.setChange(changeId);
+ }
+
+ void init(ChangeScreen2.Style style) {
+ this.style = style;
+ openForm.setVisible(Gerrit.isSignedIn());
+ }
+
+ void setReviewers(SafeHtml formatUserList) {
+ reviewers.setInnerSafeHtml(formatUserList);
+ }
+
+ @UiHandler("openForm")
+ void onOpenForm(ClickEvent e) {
+ onOpenForm();
+ }
+
+ void onOpenForm() {
+ UIObject.setVisible(form, true);
+ UIObject.setVisible(error, false);
+ openForm.setVisible(false);
+ suggestBox.setFocus(true);
+ }
+
+ @UiHandler("add")
+ void onAdd(ClickEvent e) {
+ String reviewer = suggestBox.getText();
+ if (!reviewer.isEmpty()) {
+ addReviewer(reviewer, false);
+ }
+ }
+
+ @UiHandler("cancel")
+ void onCancel(ClickEvent e) {
+ openForm.setVisible(true);
+ UIObject.setVisible(form, false);
+ suggestBox.setFocus(false);
+ }
+
+ private void addReviewer(final String reviewer, boolean confirmed) {
+ ChangeApi.reviewers(changeId.get()).post(
+ PostInput.create(reviewer, confirmed),
+ new GerritCallback<PostResult>() {
+ public void onSuccess(PostResult result) {
+ nameTxtBox.setEnabled(true);
+
+ if (result.confirm()) {
+ askForConfirmation(result.error());
+ } else if (result.error() != null) {
+ UIObject.setVisible(error, true);
+ error.setInnerText(result.error());
+ } else {
+ UIObject.setVisible(error, false);
+ error.setInnerText("");
+ nameTxtBox.setText("");
+
+ if (result.reviewers() != null
+ && result.reviewers().length() > 0) {
+ updateReviewerList();
+ }
+ }
+ }
+
+ private void askForConfirmation(String text) {
+ new ConfirmationDialog(
+ Util.C.approvalTableAddManyReviewersConfirmationDialogTitle(),
+ new SafeHtmlBuilder().append(text),
+ new ConfirmationCallback() {
+ @Override
+ public void onOk() {
+ addReviewer(reviewer, true);
+ }
+ }).center();
+ }
+
+ @Override
+ public void onFailure(Throwable err) {
+ UIObject.setVisible(error, true);
+ error.setInnerText(err instanceof StatusCodeException
+ ? ((StatusCodeException) err).getEncodedResponse()
+ : err.getMessage());
+ nameTxtBox.setEnabled(true);
+ }
+ });
+ }
+
+ private void updateReviewerList() {
+ ChangeApi.detail(changeId.get(),
+ new GerritCallback<ChangeInfo>() {
+ @Override
+ public void onSuccess(ChangeInfo result) {
+ display(result);
+ }
+ });
+ }
+
+ private void display(ChangeInfo info) {
+ Map<Integer, AccountInfo> r = new HashMap<Integer, AccountInfo>();
+ Map<Integer, AccountInfo> cc = new HashMap<Integer, AccountInfo>();
+ for (LabelInfo label : Natives.asList(info.all_labels().values())) {
+ if (label.all() != null) {
+ for (ApprovalInfo ai : Natives.asList(label.all())) {
+ (ai.value() != 0 ? r : cc).put(ai._account_id(), ai);
+ }
+ }
+ }
+ for (Integer i : r.keySet()) {
+ cc.remove(i);
+ }
+ cc.remove(info.owner()._account_id());
+ setReviewers(Labels.formatUserList(style, cc.values()));
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.ui.xml
new file mode 100644
index 0000000..568e950
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.ui.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2013 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<ui:UiBinder
+ xmlns:ui='urn:ui:com.google.gwt.uibinder'
+ xmlns:c='urn:import:com.google.gwtexpui.globalkey.client'
+ xmlns:g='urn:import:com.google.gwt.user.client.ui'>
+ <ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
+ <ui:style>
+ .openAdd {
+ cursor: pointer;
+ float: right;
+ padding: 0;
+ margin: 0;
+ border: 0;
+ background-color: transparent;
+ }
+
+ .suggestBox {
+ margin-bottom: 2px;
+ }
+
+ .error {
+ color: #D33D3D;
+ font-weight: bold;
+ }
+
+ .cancel {
+ float: right;
+ }
+ </ui:style>
+ <g:HTMLPanel>
+ <div>
+ <span ui:field='reviewers'/>
+ <g:Button ui:field='openForm'
+ title='Add reviewers to this change'
+ styleName='{style.openAdd}'
+ visible='false'>
+ <ui:attribute name='title'/>
+ <div>[+]</div>
+ </g:Button>
+ </div>
+ <div ui:field='form' style='display: none' aria-hidden='true'>
+ <g:SuggestBox ui:field='suggestBox' styleName='{style.suggestBox}'/>
+ <div ui:field='error'
+ class='{style.error}'
+ style='display: none' aria-hidden='true'/>
+ <div>
+ <g:Button ui:field='add' styleName='{res.style.button}'>
+ <div>Add</div>
+ </g:Button>
+ <g:Button ui:field='cancel'
+ styleName='{res.style.button}'
+ addStyleNames='{style.cancel}'>
+ <div>Cancel</div>
+ </g:Button>
+ </div>
+ </div>
+ </g:HTMLPanel>
+ </ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevisionsBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevisionsBox.java
index c0b2112..4ebf3819 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevisionsBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevisionsBox.java
@@ -203,7 +203,7 @@
}
private String url(RevisionInfo r) {
- return PageLinks.toChange2(
+ return PageLinks.toChange(
changeId,
String.valueOf(r._number()));
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java
index 3008830..8a3aae5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java
@@ -40,7 +40,7 @@
}
private void redisplay() {
- Gerrit.display(PageLinks.toChange2(id));
+ Gerrit.display(PageLinks.toChange(id));
}
});
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java
index fff109a..4f98bdb 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java
@@ -134,7 +134,7 @@
new GerritCallback<String>() {
@Override
public void onSuccess(String result) {
- Gerrit.display(PageLinks.toChange2(
+ Gerrit.display(PageLinks.toChange(
psId.getParentKey(),
String.valueOf(psId.get())));
}
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 93c9505..350f97f 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
@@ -254,8 +254,8 @@
}
}
- private static class PostInput extends JavaScriptObject {
- static PostInput create(String reviewer, boolean confirmed) {
+ public static class PostInput extends JavaScriptObject {
+ public static PostInput create(String reviewer, boolean confirmed) {
PostInput input = createObject().cast();
input.init(reviewer, confirmed);
return input;
@@ -272,7 +272,7 @@
}
}
- private static class ReviewerInfo extends AccountInfo {
+ public static class ReviewerInfo extends AccountInfo {
final Set<String> approvals() {
return Natives.keys(_approvals());
}
@@ -283,10 +283,10 @@
}
}
- private static class PostResult extends JavaScriptObject {
- final native JsArray<ReviewerInfo> reviewers() /*-{ return this.reviewers; }-*/;
- final native boolean confirm() /*-{ return this.confirm || false; }-*/;
- final native String error() /*-{ return this.error; }-*/;
+ public static class PostResult extends JavaScriptObject {
+ public final native JsArray<ReviewerInfo> reviewers() /*-{ return this.reviewers; }-*/;
+ public final native boolean confirm() /*-{ return this.confirm || false; }-*/;
+ public final native String error() /*-{ return this.error; }-*/;
protected PostResult() {
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
index c292154..9d76104 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
@@ -86,6 +86,12 @@
return change(id).view("reviewers");
}
+ public static RestApi suggestReviewers(int id, String q, int n) {
+ return change(id).view("suggest_reviewers")
+ .addParameter("q", q)
+ .addParameter("n", n);
+ }
+
public static RestApi reviewer(int id, int reviewer) {
return change(id).view("reviewers").id(reviewer);
}
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 197b466..81ab944 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
@@ -61,6 +61,7 @@
String keyPublishComments();
String keyEditTopic();
String keyEditMessage();
+ String keyAddReviewers();
String patchTableColumnName();
String patchTableColumnComments();
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 f51cff0..86623dc 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
@@ -41,6 +41,7 @@
keyPublishComments = Review and publish comments
keyEditTopic = Edit change topic
keyEditMessage = Edit commit message
+keyAddReviewers = Add reviewers
patchTableColumnName = File Path
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable2.java
index f993ad9..cedbd4b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable2.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable2.java
@@ -230,7 +230,8 @@
LabelInfo label = c.label(name);
if (label == null) {
- table.clearCell(row, col);
+ fmt.getElement(row, col).setTitle(Gerrit.C.labelNotApplicable());
+ fmt.addStyleName(row, col, Gerrit.RESOURCES.css().labelNotApplicable());
continue;
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java
index c46762c..e5c390c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java
@@ -73,7 +73,7 @@
this.path = path;
SafeHtml.setInnerHTML(filePath, formatPath(path));
- up.setTargetHistoryToken(PageLinks.toChange2(
+ up.setTargetHistoryToken(PageLinks.toChange(
patchSetId.getParentKey(),
String.valueOf(patchSetId.get())));
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java
index 951534b..1094dae 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java
@@ -1096,7 +1096,7 @@
public void run() {
String rev = String.valueOf(revision.get());
Gerrit.display(
- PageLinks.toChange2(changeId, rev),
+ PageLinks.toChange(changeId, rev),
new ChangeScreen2(changeId, rev, openReplyBox));
}
};
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UpToChangeCommand2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UpToChangeCommand2.java
index f40c4c1..7071e7f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UpToChangeCommand2.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UpToChangeCommand2.java
@@ -31,7 +31,7 @@
@Override
public void onKeyPress(final KeyPressEvent event) {
- Gerrit.display(PageLinks.toChange2(
+ Gerrit.display(PageLinks.toChange(
revision.getParentKey(),
String.valueOf(revision.get())));
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
index 8087b68..908c07f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
@@ -595,6 +595,9 @@
.changeTable .dataCell.singleLine {
white-space: nowrap;
}
+.changeTable .dataCell.labelNotApplicable {
+ background: #F5F5F5;
+}
.changeTable .iconHeader {
border-top: 1px solid backgroundColor;
border-bottom: 1px solid backgroundColor;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupBaseInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupBaseInfo.java
new file mode 100644
index 0000000..4811e59
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupBaseInfo.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.client.groups;
+
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.http.client.URL;
+
+public class GroupBaseInfo extends JavaScriptObject {
+ public final AccountGroup.UUID getGroupUUID() {
+ return new AccountGroup.UUID(URL.decodeQueryString(id()));
+ }
+
+ public final native String id() /*-{ return this.id; }-*/;
+ public final native String name() /*-{ return this.name; }-*/;
+
+ protected GroupBaseInfo() {
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupInfo.java
index f529990..f1e4e87 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupInfo.java
@@ -20,17 +20,11 @@
import com.google.gwt.core.client.JsArray;
import com.google.gwt.http.client.URL;
-public class GroupInfo extends JavaScriptObject {
+public class GroupInfo extends GroupBaseInfo {
public final AccountGroup.Id getGroupId() {
return new AccountGroup.Id(group_id());
}
- public final AccountGroup.UUID getGroupUUID() {
- return new AccountGroup.UUID(URL.decodeQueryString(id()));
- }
-
- public final native String id() /*-{ return this.id; }-*/;
- public final native String name() /*-{ return this.name; }-*/;
public final native GroupOptionsInfo options() /*-{ return this.options; }-*/;
public final native String description() /*-{ return this.description; }-*/;
public final native String url() /*-{ return this.url; }-*/;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
index 677a615..2980159 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
@@ -17,6 +17,7 @@
import com.google.gerrit.common.data.GerritConfig;
import com.google.gerrit.common.data.GitwebConfig;
import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AnonymousCowardName;
@@ -124,6 +125,9 @@
config.setSuggestFrom(cfg.getInt("suggest", "from", 0));
config.setChangeUpdateDelay((int) ConfigUtil.getTimeUnit(
cfg, "change", null, "updateDelay", 30, TimeUnit.SECONDS));
+ config.setChangeScreen(cfg.getEnum(
+ "gerrit", null, "changeScreen",
+ AccountGeneralPreferences.ChangeScreen.OLD_UI));
config.setReportBugUrl(cfg.getString("gerrit", null, "reportBugUrl"));
if (config.getReportBugUrl() == null) {
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 63c22ce..0f088c9 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
@@ -21,6 +21,7 @@
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtjsonrpc.common.VoidResult;
@@ -39,6 +40,7 @@
private final ReviewDb db;
private final GitRepositoryManager gitManager;
private final GitReferenceUpdated gitRefUpdated;
+ private final ChangeIndexer indexer;
private final PatchSet.Id patchSetId;
@@ -47,11 +49,13 @@
final ChangeControl.Factory changeControlFactory,
final GitRepositoryManager gitManager,
final GitReferenceUpdated gitRefUpdated,
+ final ChangeIndexer indexer,
@Assisted final PatchSet.Id patchSetId) {
this.changeControlFactory = changeControlFactory;
this.db = db;
this.gitManager = gitManager;
this.gitRefUpdated = gitRefUpdated;
+ this.indexer = indexer;
this.patchSetId = patchSetId;
}
@@ -65,7 +69,8 @@
throw new NoSuchChangeException(changeId);
}
- ChangeUtil.deleteDraftChange(patchSetId, gitManager, gitRefUpdated, db);
+ ChangeUtil.deleteDraftChange(patchSetId, gitManager, gitRefUpdated, db,
+ indexer);
return VoidResult.INSTANCE;
}
}
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index c98bd5d..13efa6c 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -42,6 +42,7 @@
import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
+import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import com.google.inject.assistedinject.Assisted;
@@ -249,7 +250,8 @@
if (!Sets.intersection(statuses, CLOSED_STATUSES).isEmpty()) {
indexes.add(closedIndex);
}
- return new QuerySource(indexes, QueryBuilder.toQuery(p), limit);
+ return new QuerySource(indexes, QueryBuilder.toQuery(schema, p), limit,
+ ChangeQueryBuilder.hasNonTrivialSortKeyAfter(schema, p));
}
@Override
@@ -270,11 +272,14 @@
private final List<SubIndex> indexes;
private final Query query;
private final int limit;
+ private final boolean reverse;
- public QuerySource(List<SubIndex> indexes, Query query, int limit) {
+ private QuerySource(List<SubIndex> indexes, Query query, int limit,
+ boolean reverse) {
this.indexes = indexes;
this.query = query;
this.limit = limit;
+ this.reverse = reverse;
}
@Override
@@ -297,9 +302,11 @@
IndexSearcher[] searchers = new IndexSearcher[indexes.size()];
Sort sort = new Sort(
new SortField(
- ChangeField.UPDATED.getName(),
- SortField.Type.INT,
- true /* descending */));
+ ChangeField.SORTKEY.getName(),
+ SortField.Type.LONG,
+ // Standard order is descending by sort key, unless reversed due
+ // to a sortkey_before predicate.
+ !reverse));
try {
TopDocs[] hits = new TopDocs[indexes.size()];
for (int i = 0; i < indexes.size(); i++) {
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java
index f491bc2..f99cce8 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java
@@ -23,6 +23,7 @@
import com.google.gerrit.server.index.FieldType;
import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.RegexPredicate;
+import com.google.gerrit.server.index.Schema;
import com.google.gerrit.server.index.TimestampRangePredicate;
import com.google.gerrit.server.query.AndPredicate;
import com.google.gerrit.server.query.NotPredicate;
@@ -54,26 +55,27 @@
return intTerm(ID_FIELD, cd.getId().get());
}
- public static Query toQuery(Predicate<ChangeData> p)
+ public static Query toQuery(Schema<ChangeData> schema, Predicate<ChangeData> p)
throws QueryParseException {
if (p instanceof AndPredicate) {
- return and(p);
+ return and(schema, p);
} else if (p instanceof OrPredicate) {
- return or(p);
+ return or(schema, p);
} else if (p instanceof NotPredicate) {
- return not(p);
+ return not(schema, p);
} else if (p instanceof IndexPredicate) {
- return fieldQuery((IndexPredicate<ChangeData>) p);
+ return fieldQuery(schema, (IndexPredicate<ChangeData>) p);
} else {
throw new QueryParseException("cannot create query for index: " + p);
}
}
- private static Query or(Predicate<ChangeData> p) throws QueryParseException {
+ private static Query or(Schema<ChangeData> schema, Predicate<ChangeData> p)
+ throws QueryParseException {
try {
BooleanQuery q = new BooleanQuery();
for (int i = 0; i < p.getChildCount(); i++) {
- q.add(toQuery(p.getChild(i)), SHOULD);
+ q.add(toQuery(schema, p.getChild(i)), SHOULD);
}
return q;
} catch (BooleanQuery.TooManyClauses e) {
@@ -81,7 +83,8 @@
}
}
- private static Query and(Predicate<ChangeData> p) throws QueryParseException {
+ private static Query and(Schema<ChangeData> schema, Predicate<ChangeData> p)
+ throws QueryParseException {
try {
BooleanQuery b = new BooleanQuery();
List<Query> not = Lists.newArrayListWithCapacity(p.getChildCount());
@@ -92,10 +95,10 @@
if (n instanceof TimestampRangePredicate) {
b.add(notTimestamp((TimestampRangePredicate<ChangeData>) n), MUST);
} else {
- not.add(toQuery(n));
+ not.add(toQuery(schema, n));
}
} else {
- b.add(toQuery(c), MUST);
+ b.add(toQuery(schema, c), MUST);
}
}
for (Query q : not) {
@@ -107,7 +110,8 @@
}
}
- private static Query not(Predicate<ChangeData> p) throws QueryParseException {
+ private static Query not(Schema<ChangeData> schema, Predicate<ChangeData> p)
+ throws QueryParseException {
Predicate<ChangeData> n = p.getChild(0);
if (n instanceof TimestampRangePredicate) {
return notTimestamp((TimestampRangePredicate<ChangeData>) n);
@@ -116,12 +120,12 @@
// Lucene does not support negation, start with all and subtract.
BooleanQuery q = new BooleanQuery();
q.add(new MatchAllDocsQuery(), MUST);
- q.add(toQuery(n), MUST_NOT);
+ q.add(toQuery(schema, n), MUST_NOT);
return q;
}
- private static Query fieldQuery(IndexPredicate<ChangeData> p)
- throws QueryParseException {
+ private static Query fieldQuery(Schema<ChangeData> schema,
+ IndexPredicate<ChangeData> p) throws QueryParseException {
if (p.getType() == FieldType.INTEGER) {
return intQuery(p);
} else if (p.getType() == FieldType.TIMESTAMP) {
@@ -133,7 +137,7 @@
} else if (p.getType() == FieldType.FULL_TEXT) {
return fullTextQuery(p);
} else if (p instanceof SortKeyPredicate) {
- return sortKeyQuery((SortKeyPredicate) p);
+ return sortKeyQuery(schema, (SortKeyPredicate) p);
} else {
throw badFieldType(p.getType());
}
@@ -158,12 +162,14 @@
return new TermQuery(intTerm(p.getField().getName(), value));
}
- private static Query sortKeyQuery(SortKeyPredicate p) {
+ private static Query sortKeyQuery(Schema<ChangeData> schema, SortKeyPredicate p) {
+ long min = p.getMinValue(schema);
+ long max = p.getMaxValue(schema);
return NumericRangeQuery.newLongRange(
p.getField().getName(),
- p.getMinValue() != Long.MIN_VALUE ? p.getMinValue() : null,
- p.getMaxValue() != Long.MAX_VALUE ? p.getMaxValue() : null,
- true, true);
+ min != Long.MIN_VALUE ? min : null,
+ max != Long.MAX_VALUE ? max : null,
+ false, false);
}
private static Query timestampQuery(IndexPredicate<ChangeData> p)
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/BaseInit.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/BaseInit.java
index b830959..4f77b0f 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/BaseInit.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/BaseInit.java
@@ -77,7 +77,8 @@
final SiteInit init = createSiteInit();
beforeInit(init);
- init.flags.autoStart = getAutoStart() && init.site.isNew;;
+ init.flags.autoStart = getAutoStart() && init.site.isNew;
+ init.flags.skipPlugins = skipPlugins();
final SiteRun run;
try {
@@ -103,6 +104,10 @@
return 0;
}
+ protected boolean skipPlugins() {
+ return false;
+ }
+
protected void beforeInit(SiteInit init) throws Exception {
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
index 1cab515..cbf50b9 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
@@ -126,6 +126,11 @@
return !noAutoStart;
}
+ @Override
+ protected boolean skipPlugins() {
+ return skipPlugins;
+ }
+
void start(SiteRun run) throws Exception {
if (run.flags.autoStart) {
if (HostPlatform.isWin32()) {
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGeneralPreferences.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGeneralPreferences.java
index 92c479e..6cc83e5 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGeneralPreferences.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGeneralPreferences.java
@@ -77,6 +77,11 @@
UNIFIED_DIFF
}
+ public static enum ChangeScreen {
+ OLD_UI,
+ CHANGE_SCREEN2
+ }
+
public static enum TimeFormat {
/** 12-hour clock: 1:15 am, 2:13 pm */
HHMM_12("h:mm a"),
@@ -144,6 +149,9 @@
@Column(id = 14, length = 20, notNull = false)
protected String diffView;
+ @Column(id = 15, length = 20, notNull = false)
+ protected String changeScreen;
+
public AccountGeneralPreferences() {
}
@@ -278,6 +286,14 @@
this.diffView = diffView.name();
}
+ public ChangeScreen getChangeScreen() {
+ return changeScreen != null ? ChangeScreen.valueOf(changeScreen) : null;
+ }
+
+ public void setChangeScreen(ChangeScreen ui) {
+ changeScreen = ui != null ? ui.name() : null;
+ }
+
public void resetToDefaults() {
maximumPageSize = DEFAULT_PAGESIZE;
showSiteHeader = true;
@@ -292,5 +308,6 @@
relativeDateInChangeTable = false;
commentVisibilityStrategy = null;
diffView = null;
+ changeScreen = null;
}
}
diff --git a/gerrit-server/BUCK b/gerrit-server/BUCK
index 1e20546..5232f41 100644
--- a/gerrit-server/BUCK
+++ b/gerrit-server/BUCK
@@ -1,4 +1,7 @@
-SRCS = glob(['src/main/java/**/*.java'])
+SRCS = glob([
+ 'src/main/java/**/*.java',
+ 'src/test/java/com/google/gerrit/server/project/Util.java'
+])
RESOURCES = glob(['src/main/resources/**/*'])
# TODO(sop) break up gerrit-server java_library(), its too big
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 f79e05f..2f1cfa0dc 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
@@ -35,6 +35,8 @@
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.validators.CommitValidationException;
import com.google.gerrit.server.git.validators.CommitValidators;
+
+import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.mail.CommitMessageEditedSender;
import com.google.gerrit.server.mail.RevertedSender;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
@@ -392,19 +394,19 @@
}
}
- public static void deleteDraftChange(final PatchSet.Id patchSetId,
+ public static void deleteDraftChange(PatchSet.Id patchSetId,
GitRepositoryManager gitManager,
- final GitReferenceUpdated gitRefUpdated, final ReviewDb db)
+ GitReferenceUpdated gitRefUpdated, ReviewDb db, ChangeIndexer indexer)
throws NoSuchChangeException, OrmException, IOException {
final Change.Id changeId = patchSetId.getParentKey();
- deleteDraftChange(changeId, gitManager, gitRefUpdated, db);
+ deleteDraftChange(changeId, gitManager, gitRefUpdated, db, indexer);
}
- public static void deleteDraftChange(final Change.Id changeId,
+ public static void deleteDraftChange(Change.Id changeId,
GitRepositoryManager gitManager,
- final GitReferenceUpdated gitRefUpdated, final ReviewDb db)
+ GitReferenceUpdated gitRefUpdated, ReviewDb db, ChangeIndexer indexer)
throws NoSuchChangeException, OrmException, IOException {
- final Change change = db.changes().get(changeId);
+ Change change = db.changes().get(changeId);
if (change == null || change.getStatus() != Change.Status.DRAFT) {
throw new NoSuchChangeException(changeId);
}
@@ -418,6 +420,7 @@
db.starredChanges().delete(db.starredChanges().byChange(changeId));
db.trackingIds().delete(db.trackingIds().byChange(changeId));
db.changes().delete(Collections.singleton(change));
+ indexer.delete(change);
}
public static void deleteOnlyDraftPatchSet(final PatchSet patch,
@@ -475,11 +478,7 @@
if ("z".equals(sortKey)) {
return Long.MAX_VALUE;
}
- String ts = sortKey.substring(0, 8);
- int i = 0;
- while (i < 8 && ts.charAt(i) == '0')
- i++;
- return Long.parseLong(ts.substring(i), 16);
+ return Long.parseLong(sortKey, 16);
}
public static void computeSortKey(final Change c) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetPreferences.java
index 733c49d..4d3b913 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetPreferences.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetPreferences.java
@@ -17,6 +17,7 @@
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
+import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.ChangeScreen;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.CommentVisibilityStrategy;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DateFormat;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DiffView;
@@ -61,6 +62,7 @@
Boolean relativeDateInChangeTable;
CommentVisibilityStrategy commentVisibilityStrategy;
DiffView diffView;
+ ChangeScreen changeScreen;
PreferenceInfo(AccountGeneralPreferences p) {
changesPerPage = p.getMaximumPageSize();
@@ -76,6 +78,7 @@
relativeDateInChangeTable = p.isRelativeDateInChangeTable() ? true : null;
commentVisibilityStrategy = p.getCommentVisibilityStrategy();
diffView = p.getDiffView();
+ changeScreen = p.getChangeScreen();
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
index 8b7b6ad..2c20b03 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
@@ -129,13 +129,19 @@
currentUser.newCommitterIdent(myIdent.getWhen(),
myIdent.getTimeZone());
+ final ObjectId computedChangeId =
+ ChangeIdUtil
+ .computeChangeId(commitToCherryPick.getTree(), mergeTip,
+ commitToCherryPick.getAuthorIdent(), myIdent, message);
+ String commitMessage = ChangeIdUtil.insertId(message, computedChangeId);
+
RevCommit cherryPickCommit;
ObjectInserter oi = git.newObjectInserter();
try {
ProjectState projectState = refControl.getProjectControl().getProjectState();
cherryPickCommit =
mergeUtilFactory.create(projectState).createCherryPickFromCommit(git, oi, mergeTip,
- commitToCherryPick, committerIdent, message, revWalk);
+ commitToCherryPick, committerIdent, commitMessage, revWalk);
} finally {
oi.release();
}
@@ -151,11 +157,6 @@
final String idStr = idList.get(idList.size() - 1).trim();
changeKey = new Change.Key(idStr);
} else {
- final ObjectId computedChangeId =
- ChangeIdUtil
- .computeChangeId(cherryPickCommit.getTree(), mergeTip,
- cherryPickCommit.getAuthorIdent(), myIdent, message);
-
changeKey = new Change.Key("I" + computedChangeId.name());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChange.java
index 7d3a012..1386e0b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChange.java
@@ -26,6 +26,7 @@
import com.google.gerrit.server.change.DeleteDraftChange.Input;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtorm.server.OrmException;
@@ -42,15 +43,18 @@
protected final Provider<ReviewDb> dbProvider;
private final GitRepositoryManager gitManager;
private final GitReferenceUpdated gitRefUpdated;
+ private final ChangeIndexer indexer;
@Inject
public DeleteDraftChange(Provider<ReviewDb> dbProvider,
GitRepositoryManager gitManager,
GitReferenceUpdated gitRefUpdated,
- PatchSetInfoFactory patchSetInfoFactory) {
+ PatchSetInfoFactory patchSetInfoFactory,
+ ChangeIndexer indexer) {
this.dbProvider = dbProvider;
this.gitManager = gitManager;
this.gitRefUpdated = gitRefUpdated;
+ this.indexer = indexer;
}
@Override
@@ -67,7 +71,7 @@
try {
ChangeUtil.deleteDraftChange(rsrc.getChange().getId(),
- gitManager, gitRefUpdated, dbProvider.get());
+ gitManager, gitRefUpdated, dbProvider.get(), indexer);
} catch (NoSuchChangeException e) {
throw new ResourceNotFoundException(e.getMessage());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
index d4e17d6..9838381 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
@@ -28,6 +28,7 @@
import com.google.gerrit.server.change.DeleteDraftPatchSet.Input;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.project.NoSuchChangeException;
@@ -47,16 +48,19 @@
private final GitRepositoryManager gitManager;
private final GitReferenceUpdated gitRefUpdated;
private final PatchSetInfoFactory patchSetInfoFactory;
+ private final ChangeIndexer indexer;
@Inject
public DeleteDraftPatchSet(Provider<ReviewDb> dbProvider,
GitRepositoryManager gitManager,
GitReferenceUpdated gitRefUpdated,
- PatchSetInfoFactory patchSetInfoFactory) {
+ PatchSetInfoFactory patchSetInfoFactory,
+ ChangeIndexer indexer) {
this.dbProvider = dbProvider;
this.gitManager = gitManager;
this.gitRefUpdated = gitRefUpdated;
this.patchSetInfoFactory = patchSetInfoFactory;
+ this.indexer = indexer;
}
@Override
@@ -129,7 +133,7 @@
throws OrmException, IOException, ResourceNotFoundException {
try {
ChangeUtil.deleteDraftChange(patchSetId,
- gitManager, gitRefUpdated, dbProvider.get());
+ gitManager, gitRefUpdated, dbProvider.get(), indexer);
} catch (NoSuchChangeException e) {
throw new ResourceNotFoundException(e.getMessage());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
index 25bd80f..d1fd730 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
@@ -59,6 +59,7 @@
post(CHANGE_KIND, "index").to(Index.class);
post(CHANGE_KIND, "reviewers").to(PostReviewers.class);
+ get(CHANGE_KIND, "suggest_reviewers").to(SuggestReviewers.class);
child(CHANGE_KIND, "reviewers").to(Reviewers.class);
get(REVIEWER_KIND).to(GetReviewer.class);
delete(REVIEWER_KIND).to(DeleteReviewer.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/SuggestReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/SuggestReviewers.java
new file mode 100644
index 0000000..1a670cc
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/SuggestReviewers.java
@@ -0,0 +1,296 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.change;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.extensions.restapi.Url;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountExternalId;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountControl;
+import com.google.gerrit.server.account.AccountInfo;
+import com.google.gerrit.server.account.AccountVisibility;
+import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.account.GroupMembers;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.group.GroupJson.GroupBaseInfo;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.lib.Config;
+import org.kohsuke.args4j.Option;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+class SuggestReviewers implements RestReadView<ChangeResource> {
+
+ private static final String MAX_SUFFIX = "\u9fa5";
+ private static final int MAX = 10;
+
+ private final AccountInfo.Loader.Factory accountLoaderFactory;
+ private final AccountControl.Factory accountControlFactory;
+ private final GroupMembers.Factory groupMembersFactory;
+ private final AccountCache accountCache;
+ private final Provider<ReviewDb> dbProvider;
+ private final Provider<CurrentUser> currentUser;
+ private final IdentifiedUser.GenericFactory identifiedUserFactory;
+ private final GroupBackend groupBackend;
+ private final boolean suggestAccounts;
+ private final int suggestFrom;
+ private final int maxAllowed;
+ private int limit;
+ private String query;
+
+ @Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT",
+ usage = "maximum number of reviewers to list")
+ public void setLimit(int l) {
+ this.limit = l <= 0 ? MAX : Math.min(l, MAX);
+ }
+
+ @Option(name = "--query", aliases = {"-q"}, metaVar = "QUERY",
+ usage = "match reviewers query")
+ public void setQuery(String q) {
+ this.query = q;
+ }
+
+ @Inject
+ SuggestReviewers(AccountVisibility av,
+ AccountInfo.Loader.Factory accountLoaderFactory,
+ AccountControl.Factory accountControlFactory,
+ AccountCache accountCache,
+ GroupMembers.Factory groupMembersFactory,
+ IdentifiedUser.GenericFactory identifiedUserFactory,
+ Provider<CurrentUser> currentUser,
+ Provider<ReviewDb> dbProvider,
+ @GerritServerConfig Config cfg,
+ GroupBackend groupBackend) {
+ this.accountLoaderFactory = accountLoaderFactory;
+ this.accountControlFactory = accountControlFactory;
+ this.accountCache = accountCache;
+ this.groupMembersFactory = groupMembersFactory;
+ this.dbProvider = dbProvider;
+ this.identifiedUserFactory = identifiedUserFactory;
+ this.currentUser = currentUser;
+ this.groupBackend = groupBackend;
+
+ String suggest = cfg.getString("suggest", null, "accounts");
+ if ("OFF".equalsIgnoreCase(suggest)
+ || "false".equalsIgnoreCase(suggest)) {
+ this.suggestAccounts = false;
+ } else {
+ this.suggestAccounts = (av != AccountVisibility.NONE);
+ }
+
+ this.suggestFrom = cfg.getInt("suggest", null, "from", 0);
+ this.maxAllowed = cfg.getInt("addreviewer", "maxAllowed",
+ PostReviewers.DEFAULT_MAX_REVIEWERS);
+ }
+
+ private interface VisibilityControl {
+ boolean isVisibleTo(Account account) throws OrmException;
+ }
+
+ @Override
+ public List<SuggestedReviewerInfo> apply(ChangeResource rsrc)
+ throws BadRequestException, OrmException, IOException {
+ if (Strings.isNullOrEmpty(query)) {
+ throw new BadRequestException("missing query field");
+ }
+
+ if (!suggestAccounts || query.length() < suggestFrom) {
+ return Collections.emptyList();
+ }
+
+ VisibilityControl visibilityControl = getVisibility(rsrc);
+ List<AccountInfo> suggestedAccounts = suggestAccount(visibilityControl);
+ accountLoaderFactory.create(true).fill(suggestedAccounts);
+
+ List<SuggestedReviewerInfo> reviewer = Lists.newArrayList();
+ for (AccountInfo a : suggestedAccounts) {
+ reviewer.add(new SuggestedReviewerInfo(a));
+ }
+
+ Project p = rsrc.getControl().getProject();
+ for (GroupReference g : suggestAccountGroup(
+ rsrc.getControl().getProjectControl())) {
+ if (suggestGroupAsReviewer(p, g, visibilityControl)) {
+ GroupBaseInfo info = new GroupBaseInfo();
+ info.id = Url.encode(g.getUUID().get());
+ info.name = g.getName();
+ reviewer.add(new SuggestedReviewerInfo(info));
+ }
+ }
+
+ Collections.sort(reviewer);
+ if (reviewer.size() <= limit) {
+ return reviewer;
+ } else {
+ return reviewer.subList(0, limit);
+ }
+ }
+
+ private VisibilityControl getVisibility(final ChangeResource rsrc) {
+ if (rsrc.getControl().getRefControl().isVisibleByRegisteredUsers()) {
+ return new VisibilityControl() {
+ @Override
+ public boolean isVisibleTo(Account account) throws OrmException {
+ return true;
+ }
+ };
+ } else {
+ return new VisibilityControl() {
+ @Override
+ public boolean isVisibleTo(Account account) throws OrmException {
+ IdentifiedUser who =
+ identifiedUserFactory.create(dbProvider, account.getId());
+ // we can't use changeControl directly as it won't suggest reviewers
+ // to drafts
+ return rsrc.getControl().forUser(who).isRefVisible();
+ }
+ };
+ }
+ }
+
+ private List<GroupReference> suggestAccountGroup(ProjectControl ctl) {
+ return Lists.newArrayList(
+ Iterables.limit(groupBackend.suggest(query, ctl), limit));
+ }
+
+ private List<AccountInfo> suggestAccount(VisibilityControl visibilityControl)
+ throws OrmException {
+ String a = query;
+ String b = a + MAX_SUFFIX;
+
+ LinkedHashMap<Account.Id, AccountInfo> r = Maps.newLinkedHashMap();
+ for (Account p : dbProvider.get().accounts()
+ .suggestByFullName(a, b, limit)) {
+ addSuggestion(r, p, new AccountInfo(p.getId()), visibilityControl);
+ }
+
+ if (r.size() < limit) {
+ for (Account p : dbProvider.get().accounts()
+ .suggestByPreferredEmail(a, b, limit - r.size())) {
+ addSuggestion(r, p, new AccountInfo(p.getId()), visibilityControl);
+ }
+ }
+
+ if (r.size() < limit) {
+ for (AccountExternalId e : dbProvider.get().accountExternalIds()
+ .suggestByEmailAddress(a, b, limit - r.size())) {
+ if (!r.containsKey(e.getAccountId())) {
+ Account p = accountCache.get(e.getAccountId()).getAccount();
+ AccountInfo info = new AccountInfo(p.getId());
+ addSuggestion(r, p, info, visibilityControl);
+ }
+ }
+ }
+
+ return Lists.newArrayList(r.values());
+ }
+
+ private void addSuggestion(Map<Account.Id, AccountInfo> map, Account account,
+ AccountInfo info, VisibilityControl visibilityControl)
+ throws OrmException {
+ if (!map.containsKey(account.getId())
+ && account.isActive()
+ // Can the suggestion see the change?
+ && visibilityControl.isVisibleTo(account)
+ // Can the account see the current user?
+ && accountControlFactory.get().canSee(account)) {
+ map.put(account.getId(), info);
+ }
+ }
+
+ private boolean suggestGroupAsReviewer(Project project,
+ GroupReference group, VisibilityControl visibilityControl)
+ throws OrmException, IOException {
+ if (!PostReviewers.isLegalReviewerGroup(group.getUUID())) {
+ return false;
+ }
+
+ try {
+ Set<Account> members = groupMembersFactory
+ .create(currentUser.get())
+ .listAccounts(group.getUUID(), project.getNameKey());
+
+ if (members.isEmpty()) {
+ return false;
+ }
+
+ if (maxAllowed > 0 && members.size() > maxAllowed) {
+ return false;
+ }
+
+ // require that at least one member in the group can see the change
+ for (Account account : members) {
+ if (visibilityControl.isVisibleTo(account)) {
+ return true;
+ }
+ }
+ } catch (NoSuchGroupException e) {
+ return false;
+ } catch (NoSuchProjectException e) {
+ return false;
+ }
+
+ return false;
+ }
+
+ static class SuggestedReviewerInfo implements Comparable<SuggestedReviewerInfo> {
+ String kind = "gerritcodereview#suggestedreviewer";
+ AccountInfo account;
+ GroupBaseInfo group;
+
+ SuggestedReviewerInfo(AccountInfo a) {
+ this.account = a;
+ }
+
+ SuggestedReviewerInfo(GroupBaseInfo g) {
+ this.group = g;
+ }
+
+ @Override
+ public int compareTo(SuggestedReviewerInfo o) {
+ return getSortValue().compareTo(o.getSortValue());
+ }
+
+ private String getSortValue() {
+ return account != null
+ ? Objects.firstNonNull(account.email,
+ Strings.nullToEmpty(account.name))
+ : Strings.nullToEmpty(group.name);
+ }
+ }
+}
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
index d20b0f6..e6ce4c2 100644
--- 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
@@ -22,6 +22,7 @@
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.project.ChangeControl;
@@ -46,6 +47,7 @@
private final GitRepositoryManager gitManager;
private final GitReferenceUpdated gitRefUpdated;
private final PatchSetInfoFactory patchSetInfoFactory;
+ private final ChangeIndexer indexer;
private final PatchSet.Id patchSetId;
@@ -53,12 +55,14 @@
DeleteDraftPatchSet(ChangeControl.Factory changeControlFactory,
ReviewDb db, GitRepositoryManager gitManager,
GitReferenceUpdated gitRefUpdated, PatchSetInfoFactory patchSetInfoFactory,
+ ChangeIndexer indexer,
@Assisted final PatchSet.Id patchSetId) {
this.changeControlFactory = changeControlFactory;
this.db = db;
this.gitManager = gitManager;
this.gitRefUpdated = gitRefUpdated;
this.patchSetInfoFactory = patchSetInfoFactory;
+ this.indexer = indexer;
this.patchSetId = patchSetId;
}
@@ -97,7 +101,8 @@
List<PatchSet> restOfPatches = db.patchSets().byChange(changeId).toList();
if (restOfPatches.size() == 0) {
try {
- ChangeUtil.deleteDraftChange(patchSetId, gitManager, gitRefUpdated, db);
+ ChangeUtil.deleteDraftChange(patchSetId, gitManager, gitRefUpdated, db,
+ indexer);
result.setChangeId(null);
} catch (IOException e) {
result.addError(new ReviewResult.Error(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeAlways.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeAlways.java
index 493a40f..2992bd9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeAlways.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeAlways.java
@@ -25,10 +25,15 @@
}
@Override
- protected CodeReviewCommit _run(final CodeReviewCommit mergeTip,
- final List<CodeReviewCommit> toMerge) throws MergeException {
+ protected CodeReviewCommit _run(CodeReviewCommit mergeTip,
+ List<CodeReviewCommit> toMerge) throws MergeException {
args.mergeUtil.reduceToMinimalMerge(args.mergeSorter, toMerge);
+ if (mergeTip == null) {
+ // The branch is unborn. Take a fast-forward resolution to
+ // create the branch.
+ mergeTip = toMerge.remove(0);
+ }
CodeReviewCommit newMergeTip = mergeTip;
while (!toMerge.isEmpty()) {
newMergeTip =
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeIfNecessary.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeIfNecessary.java
index c31edb2..3a326fc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeIfNecessary.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeIfNecessary.java
@@ -25,9 +25,16 @@
}
@Override
- protected CodeReviewCommit _run(final CodeReviewCommit mergeTip,
- final List<CodeReviewCommit> toMerge) throws MergeException {
+ protected CodeReviewCommit _run(CodeReviewCommit mergeTip,
+ List<CodeReviewCommit> toMerge) throws MergeException {
args.mergeUtil.reduceToMinimalMerge(args.mergeSorter, toMerge);
+
+ if (mergeTip == null) {
+ // The branch is unborn. Take a fast-forward resolution to
+ // create the branch.
+ mergeTip = toMerge.remove(0);
+ }
+
CodeReviewCommit newMergeTip =
args.mergeUtil.getFirstFastForward(mergeTip, args.rw, toMerge);
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 5245957..673c528 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
@@ -1253,6 +1253,11 @@
walk.markStart(walk.parseCommit(magicBranch.cmd.getNewId()));
if (magicBranch.baseCommit != null) {
walk.markUninteresting(magicBranch.baseCommit);
+ assert magicBranch.ctl != null;
+ Ref targetRef = allRefs.get(magicBranch.ctl.getRefName());
+ if (targetRef != null) {
+ walk.markUninteresting(walk.parseCommit(targetRef.getObjectId()));
+ }
} else {
markHeadsAsUninteresting(
walk,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupJson.java
index a74e6ef..8c9056d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupJson.java
@@ -126,10 +126,8 @@
}
}
- public static class GroupInfo {
+ public static class GroupInfo extends GroupBaseInfo {
final String kind = "gerritcodereview#group";
- public String id;
- public String name;
public String url;
public GroupOptionsInfo options;
@@ -143,4 +141,9 @@
public List<AccountInfo> members;
public List<GroupInfo> includes;
}
+
+ public static class GroupBaseInfo {
+ public String id;
+ public String name;
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
index 20b61e7..cfa4644 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
@@ -125,8 +125,32 @@
}
};
- /** Sort key field, duplicates {@link #UPDATED}. */
@Deprecated
+ public static long legacyParseSortKey(String sortKey) {
+ if ("z".equals(sortKey)) {
+ return Long.MAX_VALUE;
+ }
+ return Long.parseLong(sortKey.substring(0, 8), 16);
+ }
+
+ /** Legacy sort key field. */
+ @Deprecated
+ public static final FieldDef<ChangeData, Long> LEGACY_SORTKEY =
+ new FieldDef.Single<ChangeData, Long>(
+ "sortkey", FieldType.LONG, true) {
+ @Override
+ public Long get(ChangeData input, FillArgs args)
+ throws OrmException {
+ return legacyParseSortKey(input.change(args.db).getSortKey());
+ }
+ };
+
+ /**
+ * Sort key field.
+ * <p>
+ * Redundant with {@link #UPDATED} and {@link #LEGACY_ID}, but secondary index
+ * implementations may not be able to search over tuples of values.
+ */
public static final FieldDef<ChangeData, Long> SORTKEY =
new FieldDef.Single<ChangeData, Long>(
"sortkey", FieldType.LONG, true) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndex.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndex.java
index b67faa3..8e89507 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndex.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndex.java
@@ -132,7 +132,11 @@
* or NOT predicates as internal nodes, and {@link IndexPredicate}s as
* leaves.
* @param limit maximum number of results to return.
- * @return a source of documents matching the predicate.
+ * @return a source of documents matching the predicate. Documents must be
+ * returned in descending sort key order, unless a {@code sortkey_after}
+ * predicate (with a cut point not at {@link Long#MAX_VALUE}) is provided,
+ * in which case the source should return documents in ascending sort key
+ * order starting from the sort key cut point.
*
* @throws QueryParseException if the predicate could not be converted to an
* indexed data source.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexer.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexer.java
index 0aa3fc8..451b0d6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexer.java
@@ -41,6 +41,7 @@
return Futures.immediateFuture(null);
}
+ @Override
public Callable<Void> indexTask(ChangeData cd) {
return new Callable<Void>() {
@Override
@@ -49,6 +50,16 @@
}
};
}
+
+ @Override
+ public Callable<Void> deleteTask(ChangeData cd) {
+ return new Callable<Void>() {
+ @Override
+ public Void call() {
+ return null;
+ }
+ };
+ }
};
private final ListeningScheduledExecutorService executor;
@@ -71,12 +82,45 @@
* Start indexing a change.
*
* @param change change to index.
- * @param prop propagator to wrap any created runnables in.
* @return future for the indexing task.
*/
public ListenableFuture<?> index(ChangeData cd) {
return executor.submit(indexTask(cd));
}
+ /**
+ * Create a runnable to index a change.
+ *
+ * @param cd change to index.
+ * @return unstarted runnable to index the change.
+ */
public abstract Callable<Void> indexTask(ChangeData cd);
+
+ /**
+ * Start deleting a change.
+ *
+ * @param change change to delete.
+ * @return future for the deleting task.
+ */
+ public ListenableFuture<?> delete(Change change) {
+ return delete(new ChangeData(change));
+ }
+
+ /**
+ * Start deleting a change.
+ *
+ * @param change change to delete.
+ * @return future for the deleting task.
+ */
+ public ListenableFuture<?> delete(ChangeData cd) {
+ return executor.submit(deleteTask(cd));
+ }
+
+ /**
+ * Create a runnable to delete a change.
+ *
+ * @param cd change to delete.
+ * @return unstarted runnable to delete the change.
+ */
+ public abstract Callable<Void> deleteTask(ChangeData cd);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexerImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexerImpl.java
index 1de5c99..715e96b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexerImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexerImpl.java
@@ -30,6 +30,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.IOException;
import java.util.concurrent.Callable;
/**
@@ -73,14 +74,21 @@
@Override
public Callable<Void> indexTask(ChangeData cd) {
- return new Task(cd);
+ return new Task(cd, false);
+ }
+
+ @Override
+ public Callable<Void> deleteTask(ChangeData cd) {
+ return new Task(cd, true);
}
private class Task implements Callable<Void> {
private final ChangeData cd;
+ private final boolean delete;
- private Task(ChangeData cd) {
+ private Task(ChangeData cd, boolean delete) {
this.cd = cd;
+ this.delete = delete;
}
@Override
@@ -101,10 +109,10 @@
});
if (indexes != null) {
for (ChangeIndex i : indexes.getWriteIndexes()) {
- i.replace(cd); // TODO(dborowitz): Parallelize these
+ apply(i, cd); // TODO(dborowitz): Parallelize these
}
} else {
- index.replace(cd);
+ apply(index, cd);
}
return null;
} finally {
@@ -119,6 +127,14 @@
}
}
+ private void apply(ChangeIndex i, ChangeData cd) throws IOException {
+ if (delete) {
+ i.delete(cd);
+ } else {
+ i.replace(cd);
+ }
+ }
+
@Override
public String toString() {
return "index-change-" + cd.getId().get();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
index 1ed6c47..39a29fb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
@@ -38,7 +38,7 @@
ChangeField.REF,
ChangeField.TOPIC,
ChangeField.UPDATED,
- ChangeField.SORTKEY,
+ ChangeField.LEGACY_SORTKEY,
ChangeField.FILE,
ChangeField.OWNER,
ChangeField.REVIEWER,
@@ -58,6 +58,28 @@
ChangeField.REF,
ChangeField.TOPIC,
ChangeField.UPDATED,
+ ChangeField.LEGACY_SORTKEY,
+ ChangeField.FILE,
+ ChangeField.OWNER,
+ ChangeField.REVIEWER,
+ ChangeField.COMMIT,
+ ChangeField.TR,
+ ChangeField.LABEL,
+ ChangeField.REVIEWED,
+ ChangeField.COMMIT_MESSAGE,
+ ChangeField.COMMENT,
+ ChangeField.CHANGE,
+ ChangeField.APPROVAL);
+
+ @SuppressWarnings("unchecked")
+ static final Schema<ChangeData> V3 = release(
+ ChangeField.LEGACY_ID,
+ ChangeField.ID,
+ ChangeField.STATUS,
+ ChangeField.PROJECT,
+ ChangeField.REF,
+ ChangeField.TOPIC,
+ ChangeField.UPDATED,
ChangeField.SORTKEY,
ChangeField.FILE,
ChangeField.OWNER,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java
index aab6c64..bbc4773 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java
@@ -154,7 +154,7 @@
Predicate<ChangeData> out = rewriteImpl(in, index, limit);
if (in == out || out instanceof IndexPredicate) {
- return new IndexedChangeQuery(index, out, limit);
+ return new IndexedChangeQuery(db, index, out, limit);
} else if (out == null /* cannot rewrite */) {
return in;
} else {
@@ -231,7 +231,7 @@
if (isIndexed.cardinality() == 1) {
int i = isIndexed.nextSetBit(0);
newChildren.add(
- 0, new IndexedChangeQuery(index, newChildren.remove(i), limit));
+ 0, new IndexedChangeQuery(db, index, newChildren.remove(i), limit));
return copy(in, newChildren);
}
@@ -251,7 +251,7 @@
all.add(c);
}
}
- all.add(0, new IndexedChangeQuery(index, in.copy(indexed), limit));
+ all.add(0, new IndexedChangeQuery(db, index, in.copy(indexed), limit));
return copy(in, all);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java
index ccd6c89..2185af3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java
@@ -14,17 +14,22 @@
package com.google.gerrit.server.index;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
-
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
+import com.google.gerrit.server.query.change.Paginated;
+import com.google.gerrit.server.query.change.SortKeyPredicate;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
+import com.google.inject.Provider;
import java.util.Collection;
import java.util.Iterator;
@@ -39,16 +44,55 @@
* {@link ChangeDataSource} to be chosen by the query processor.
*/
public class IndexedChangeQuery extends Predicate<ChangeData>
- implements ChangeDataSource {
- private final Predicate<ChangeData> pred;
- private final int limit;
- private final ChangeDataSource source;
+ implements ChangeDataSource, Paginated {
- public IndexedChangeQuery(ChangeIndex index, Predicate<ChangeData> pred, int limit)
- throws QueryParseException {
+ /**
+ * Replace all {@link SortKeyPredicate}s in a tree.
+ * <p>
+ * Strictly speaking this should replace only the {@link SortKeyPredicate} at
+ * the top-level AND node, but this implementation is simpler, and the
+ * behavior of having multiple sortkey operators is undefined anyway.
+ *
+ * @param p predicate to replace in.
+ * @param newValue new cut value to replace all sortkey operators with.
+ * @return a copy of {@code p} with all sortkey predicates replaced; or p
+ * itself.
+ */
+ @VisibleForTesting
+ static Predicate<ChangeData> replaceSortKeyPredicates(
+ Predicate<ChangeData> p, String newValue) {
+ if (p instanceof SortKeyPredicate) {
+ return ((SortKeyPredicate) p).copy(newValue);
+ } else if (p.getChildCount() > 0) {
+ List<Predicate<ChangeData>> newChildren =
+ Lists.newArrayListWithCapacity(p.getChildCount());
+ boolean replaced = false;
+ for (Predicate<ChangeData> c : p.getChildren()) {
+ Predicate<ChangeData> nc = replaceSortKeyPredicates(c, newValue);
+ newChildren.add(nc);
+ if (nc != c) {
+ replaced = true;
+ }
+ }
+ return replaced ? p.copy(newChildren) : p;
+ } else {
+ return p;
+ }
+ }
+
+ private final Provider<ReviewDb> db;
+ private final ChangeIndex index;
+ private final int limit;
+
+ private Predicate<ChangeData> pred;
+ private ChangeDataSource source;
+
+ public IndexedChangeQuery(Provider<ReviewDb> db, ChangeIndex index,
+ Predicate<ChangeData> pred, int limit) throws QueryParseException {
+ this.db = db;
+ this.index = index;
this.pred = pred;
this.limit = limit;
- this.source = index.getSource(pred, limit);
}
@Override
@@ -70,18 +114,32 @@
}
@Override
+ public int limit() {
+ return limit;
+ }
+
+ @Override
public int getCardinality() {
- return source.getCardinality();
+ return source != null ? source.getCardinality() : limit();
}
@Override
public boolean hasChange() {
- return source.hasChange();
+ return index.getSchema().getFields()
+ .containsKey(ChangeField.CHANGE.getName());
}
@Override
public ResultSet<ChangeData> read() throws OrmException {
+ final ChangeDataSource currSource;
+ try {
+ currSource = index.getSource(pred, limit);
+ } catch (QueryParseException e) {
+ throw new OrmException(e);
+ }
+ source = currSource;
final ResultSet<ChangeData> rs = source.read();
+
return new ResultSet<ChangeData>() {
@Override
public Iterator<ChangeData> iterator() {
@@ -91,7 +149,7 @@
@Override
public
ChangeData apply(ChangeData input) {
- input.cacheFromSource(source);
+ input.cacheFromSource(currSource);
return input;
}
}).iterator();
@@ -101,7 +159,7 @@
public List<ChangeData> toList() {
List<ChangeData> r = rs.toList();
for (ChangeData cd : r) {
- cd.cacheFromSource(source);
+ cd.cacheFromSource(currSource);
}
return r;
}
@@ -114,6 +172,12 @@
}
@Override
+ public ResultSet<ChangeData> restart(ChangeData last) throws OrmException {
+ pred = replaceSortKeyPredicates(pred, last.change(db).getSortKey());
+ return read();
+ }
+
+ @Override
public Predicate<ChangeData> copy(
Collection<? extends Predicate<ChangeData>> children) {
return this;
@@ -121,7 +185,7 @@
@Override
public boolean match(ChangeData cd) throws OrmException {
- return cd.isFromSource(source) || pred.match(cd);
+ return (source != null && cd.isFromSource(source)) || pred.match(cd);
}
@Override
@@ -150,7 +214,7 @@
@Override
public String toString() {
return Objects.toStringHelper("index")
- .add("p", source)
+ .add("p", pred)
.add("limit", limit)
.toString();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
index fa2c281..d61fea4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
@@ -131,32 +131,34 @@
public void installPluginFromStream(String name, InputStream in)
throws IOException, PluginInstallException {
- if (!name.endsWith(".jar")) {
- name += ".jar";
+ String fileName = name;
+ if (!fileName.endsWith(".jar")) {
+ fileName += ".jar";
}
- File jar = new File(pluginsDir, name);
- name = nameOf(jar);
+ File dst = new File(pluginsDir, fileName);
+ name = nameOf(dst);
- File old = new File(pluginsDir, ".last_" + name + ".zip");
- File tmp = asTemp(in, ".next_" + name, ".zip", pluginsDir);
+ File tmp = asTemp(in, ".next_" + fileName + "_", ".tmp", pluginsDir);
synchronized (this) {
Plugin active = running.get(name);
if (active != null) {
- log.info(String.format("Replacing plugin %s", name));
+ fileName = active.getSrcFile().getName();
+ log.info(String.format("Replacing plugin %s", active.getName()));
+ File old = new File(pluginsDir, ".last_" + fileName);
old.delete();
- jar.renameTo(old);
+ active.getSrcFile().renameTo(old);
}
- new File(pluginsDir, name + ".jar.disabled").delete();
- tmp.renameTo(jar);
+ new File(pluginsDir, fileName + ".disabled").delete();
+ tmp.renameTo(dst);
try {
- runPlugin(name, jar, active);
+ Plugin plugin = runPlugin(name, dst, active);
if (active == null) {
- log.info(String.format("Installed plugin %s", name));
+ log.info(String.format("Installed plugin %s", plugin.getName()));
}
} catch (PluginInstallException e) {
- jar.delete();
+ dst.delete();
throw e;
}
@@ -214,8 +216,8 @@
continue;
}
- log.info(String.format("Disabling plugin %s", name));
- File off = new File(pluginsDir, active.getName() + ".jar.disabled");
+ log.info(String.format("Disabling plugin %s", active.getName()));
+ File off = new File(active.getSrcFile() + ".disabled");
active.getSrcFile().renameTo(off);
unloadPlugin(active);
@@ -225,7 +227,8 @@
disabled.put(name, offPlugin);
} catch (Throwable e) {
// This shouldn't happen, as the plugin was loaded earlier.
- log.warn(String.format("Cannot load disabled plugin %s", name),
+ log.warn(String.format(
+ "Cannot load disabled plugin %s", active.getName()),
e.getCause());
}
}
@@ -242,7 +245,11 @@
}
log.info(String.format("Enabling plugin %s", name));
- File on = new File(pluginsDir, off.getName() + ".jar");
+ String n = off.getSrcFile().getName();
+ if (n.endsWith(".disabled")) {
+ n = n.substring(0, n.lastIndexOf('.'));
+ }
+ File on = new File(pluginsDir, n);
off.getSrcFile().renameTo(on);
disabled.remove(name);
@@ -339,13 +346,13 @@
}
if (active != null) {
- log.info(String.format("Reloading plugin %s", name));
+ log.info(String.format("Reloading plugin %s", active.getName()));
}
try {
Plugin loadedPlugin = runPlugin(name, jar, active);
if (active == null && !loadedPlugin.isDisabled()) {
- log.info(String.format("Loaded plugin %s", name));
+ log.info(String.format("Loaded plugin %s", loadedPlugin.getName()));
}
} catch (PluginInstallException e) {
log.warn(String.format("Cannot load plugin %s", name), e.getCause());
@@ -532,6 +539,8 @@
public boolean accept(File pathname) {
String n = pathname.getName();
return (n.endsWith(".jar") || n.endsWith(".jar.disabled"))
+ && !n.startsWith(".last_")
+ && !n.startsWith(".next_")
&& pathname.isFile();
}
});
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 7684484..39abb59 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
@@ -34,6 +34,7 @@
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.index.ChangeIndex;
import com.google.gerrit.server.index.IndexCollection;
+import com.google.gerrit.server.index.Schema;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ProjectCache;
@@ -121,6 +122,13 @@
return ((IntPredicate<?>) find(p, IntPredicate.class, FIELD_LIMIT)).intValue();
}
+ public static boolean hasNonTrivialSortKeyAfter(Schema<ChangeData> schema,
+ Predicate<ChangeData> p) {
+ SortKeyPredicate after =
+ (SortKeyPredicate) find(p, SortKeyPredicate.class, "sortkey_after");
+ return after != null && after.getMaxValue(schema) > 0;
+ }
+
public static boolean hasSortKey(Predicate<ChangeData> p) {
return find(p, SortKeyPredicate.class, "sortkey_after") != null
|| find(p, SortKeyPredicate.class, "sortkey_before") != null;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/Paginated.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/Paginated.java
index 2b3edf7..d9ff80c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/Paginated.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/Paginated.java
@@ -17,7 +17,7 @@
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
-interface Paginated {
+public interface Paginated {
int limit();
ResultSet<ChangeData> restart(ChangeData last) throws OrmException;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SortKeyPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SortKeyPredicate.java
index 35a42f6..a502746 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SortKeyPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SortKeyPredicate.java
@@ -18,14 +18,25 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.index.ChangeField;
+import com.google.gerrit.server.index.FieldDef;
import com.google.gerrit.server.index.IndexPredicate;
+import com.google.gerrit.server.index.Schema;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Provider;
public abstract class SortKeyPredicate extends IndexPredicate<ChangeData> {
+ @SuppressWarnings("deprecation")
+ private static long parseSortKey(Schema<ChangeData> schema, String value) {
+ FieldDef<ChangeData, ?> field = schema.getFields().get(ChangeField.SORTKEY.getName());
+ if (field == ChangeField.SORTKEY) {
+ return ChangeUtil.parseSortKey(value);
+ } else {
+ return ChangeField.legacyParseSortKey(value);
+ }
+ }
+
protected final Provider<ReviewDb> dbProvider;
- @SuppressWarnings("deprecation")
SortKeyPredicate(Provider<ReviewDb> dbProvider, String name, String value) {
super(ChangeField.SORTKEY, name, value);
this.dbProvider = dbProvider;
@@ -36,8 +47,9 @@
return 1;
}
- public abstract long getMinValue();
- public abstract long getMaxValue();
+ public abstract long getMinValue(Schema<ChangeData> schema);
+ public abstract long getMaxValue(Schema<ChangeData> schema);
+ public abstract SortKeyPredicate copy(String newValue);
public static class Before extends SortKeyPredicate {
Before(Provider<ReviewDb> dbProvider, String value) {
@@ -45,13 +57,13 @@
}
@Override
- public long getMinValue() {
+ public long getMinValue(Schema<ChangeData> schema) {
return 0;
}
@Override
- public long getMaxValue() {
- return ChangeUtil.parseSortKey(getValue());
+ public long getMaxValue(Schema<ChangeData> schema) {
+ return parseSortKey(schema, getValue());
}
@Override
@@ -59,6 +71,11 @@
Change change = cd.change(dbProvider);
return change != null && change.getSortKey().compareTo(getValue()) < 0;
}
+
+ @Override
+ public Before copy(String newValue) {
+ return new Before(dbProvider, newValue);
+ }
}
public static class After extends SortKeyPredicate {
@@ -67,12 +84,12 @@
}
@Override
- public long getMinValue() {
- return ChangeUtil.parseSortKey(getValue());
+ public long getMinValue(Schema<ChangeData> schema) {
+ return parseSortKey(schema, getValue());
}
@Override
- public long getMaxValue() {
+ public long getMaxValue(Schema<ChangeData> schema) {
return Long.MAX_VALUE;
}
@@ -81,5 +98,10 @@
Change change = cd.change(dbProvider);
return change != null && change.getSortKey().compareTo(getValue()) > 0;
}
+
+ @Override
+ public After copy(String newValue) {
+ return new After(dbProvider, newValue);
+ }
}
}
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 99bc817..2882d00 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_83> C = Schema_83.class;
+ public static final Class<Schema_84> C = Schema_84.class;
public static class Module extends AbstractModule {
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_84.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_84.java
new file mode 100644
index 0000000..c96f650
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_84.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.schema;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+public class Schema_84 extends SchemaVersion {
+
+ @Inject
+ Schema_84(Provider<Schema_83> prior) {
+ super(prior);
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java b/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
index 6c605d2..4cd05ea 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
@@ -14,137 +14,77 @@
package com.google.gerrit.rules;
+import static com.google.gerrit.common.data.Permission.LABEL;
+import static com.google.gerrit.server.project.Util.value;
+import static com.google.gerrit.server.project.Util.category;
+import static com.google.gerrit.server.project.Util.REGISTERED;
+import static com.google.gerrit.server.project.Util.grant;
+
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.project.Util;
import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelTypes;
-import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.git.ProjectConfig;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectState;
import com.google.inject.AbstractModule;
import java.util.Arrays;
-import java.util.Set;
public class GerritCommonTest extends PrologTestCase {
- private Projects projects;
+ private final LabelType V = category("Verified",
+ value(1, "Verified"),
+ value(0, "No score"),
+ value(-1, "Fails"));
+ private final LabelType Q = category("Qualified",
+ value(1, "Qualified"),
+ value(0, "No score"),
+ value(-1, "Fails"));
+
+ private final Project.NameKey localKey = new Project.NameKey("local");
+ private ProjectConfig local;
+ private Util util;
@Override
public void setUp() throws Exception {
super.setUp();
- projects = new Projects(new LabelTypes(Arrays.asList(
- category("Code-Review",
- value(2, "Looks good to me, approved"),
- value(1, "Looks good to me, but someone else must approve"),
- value(0, "No score"),
- value(-1, "I would prefer that you didn't submit this"),
- value(-2, "Do not submit")),
- category("Verified", value(1, "Verified"),
- value(0, "No score"), value(-1, "Fails")))));
+ util = new Util();
load("gerrit", "gerrit_common_test.pl", new AbstractModule() {
@Override
protected void configure() {
bind(PrologEnvironment.Args.class).toInstance(
- new PrologEnvironment.Args(
- projects,
- null,
- null,
- null,
- null,
- null
- ));
+ new PrologEnvironment.Args(
+ null,
+ null,
+ null,
+ null,
+ null,
+ null));
}
});
+
+ local = new ProjectConfig(localKey);
+ local.createInMemory();
+ Q.setRefPatterns(Arrays.asList("refs/heads/develop"));
+
+ local.getLabelSections().put(V.getName(), V);
+ local.getLabelSections().put(Q.getName(), Q);
+ util.add(local);
+ grant(local, LABEL + V.getName(), -1, +1, REGISTERED, "refs/heads/*");
+ grant(local, LABEL + Q.getName(), -1, +1, REGISTERED, "refs/heads/master");
}
@Override
protected void setUpEnvironment(PrologEnvironment env) {
- env.set(StoredValues.CHANGE, new Change(
- new Change.Key("Ibeef"), new Change.Id(1), new Account.Id(2),
- new Branch.NameKey(projects.allProjectsName, "master")));
+ Change change =
+ new Change(new Change.Key("Ibeef"), new Change.Id(1),
+ new Account.Id(2),
+ new Branch.NameKey(localKey, "refs/heads/master"));
+ env.set(StoredValues.CHANGE, change);
+ env.set(StoredValues.CHANGE_CONTROL, util.user(local).controlFor(change));
}
public void testGerritCommon() {
runPrologBasedTests();
}
-
- private static LabelValue value(int value, String text) {
- return new LabelValue((short) value, text);
- }
-
- private static LabelType category(String name, LabelValue... values) {
- return new LabelType(name, Arrays.asList(values));
- }
-
- private static class Projects implements ProjectCache {
- private final AllProjectsName allProjectsName;
- private final ProjectState allProjects;
-
- private Projects(LabelTypes labelTypes) {
- allProjectsName = new AllProjectsName("All-Projects");
- ProjectConfig config = new ProjectConfig(allProjectsName);
- config.createInMemory();
- for (LabelType label : labelTypes.getLabelTypes()) {
- config.getLabelSections().put(label.getName(), label);
- }
- allProjects = new ProjectState(null, this, allProjectsName, null, null,
- null, null, null, config);
- }
-
- @Override
- public ProjectState getAllProjects() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public ProjectState get(Project.NameKey projectName) {
- assertEquals(allProjectsName, projectName);
- return allProjects;
- }
-
- @Override
- public ProjectState checkedGet(Project.NameKey projectName) {
- return get(projectName);
- }
-
- @Override
- public void evict(Project p) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void evict(Project.NameKey p) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void remove(Project p) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Iterable<Project.NameKey> all() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Set<AccountGroup.UUID> guessRelevantGroupUUIDs() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Iterable<Project.NameKey> byName(String prefix) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void onCreateProject(Project.NameKey newProjectName) {
- throw new UnsupportedOperationException();
- }
- }
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeIndex.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeIndex.java
new file mode 100644
index 0000000..88d6b85
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeIndex.java
@@ -0,0 +1,109 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.package com.google.gerrit.server.git;
+
+package com.google.gerrit.server.index;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.gerrit.server.query.Predicate;
+import com.google.gerrit.server.query.QueryParseException;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.ChangeDataSource;
+import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.ResultSet;
+
+class FakeIndex implements ChangeIndex {
+ static Schema<ChangeData> V1 = new Schema<ChangeData>(1, false,
+ ImmutableList.<FieldDef<ChangeData, ?>> of(
+ ChangeField.STATUS));
+
+ static Schema<ChangeData> V2 = new Schema<ChangeData>(2, false,
+ ImmutableList.of(
+ ChangeField.STATUS,
+ ChangeField.FILE));
+
+ private static class Source implements ChangeDataSource {
+ private final Predicate<ChangeData> p;
+
+ Source(Predicate<ChangeData> p) {
+ this.p = p;
+ }
+
+ @Override
+ public int getCardinality() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasChange() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ResultSet<ChangeData> read() throws OrmException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String toString() {
+ return p.toString();
+ }
+ }
+
+ private final Schema<ChangeData> schema;
+
+ FakeIndex(Schema<ChangeData> schema) {
+ this.schema = schema;
+ }
+
+ @Override
+ public ListenableFuture<Void> insert(ChangeData cd) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListenableFuture<Void> replace(ChangeData cd) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListenableFuture<Void> delete(ChangeData cd) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void deleteAll() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ChangeDataSource getSource(Predicate<ChangeData> p, int limit)
+ throws QueryParseException {
+ return new FakeIndex.Source(p);
+ }
+
+ @Override
+ public Schema<ChangeData> getSchema() {
+ return schema;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public void markReady(boolean ready) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
new file mode 100644
index 0000000..f9ef2dd
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.package com.google.gerrit.server.git;
+
+package com.google.gerrit.server.index;
+
+import com.google.gerrit.server.query.OperatorPredicate;
+import com.google.gerrit.server.query.Predicate;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.ChangeQueryBuilder;
+import com.google.gwtorm.server.OrmException;
+
+public class FakeQueryBuilder extends ChangeQueryBuilder {
+ FakeQueryBuilder(IndexCollection indexes) {
+ super(
+ new FakeQueryBuilder.Definition<ChangeData, FakeQueryBuilder>(
+ FakeQueryBuilder.class),
+ new ChangeQueryBuilder.Arguments(null, null, null, null, null, null,
+ null, null, null, null, null, indexes),
+ null);
+ }
+
+ @Operator
+ public Predicate<ChangeData> foo(String value) {
+ return predicate("foo", value);
+ }
+
+ @Operator
+ public Predicate<ChangeData> bar(String value) {
+ return predicate("bar", value);
+ }
+
+ private Predicate<ChangeData> predicate(String name, String value) {
+ return new OperatorPredicate<ChangeData>(name, value) {
+ @Override
+ public boolean match(ChangeData object) throws OrmException {
+ return false;
+ }
+
+ @Override
+ public int getCost() {
+ return 0;
+ }
+ };
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java
index bc96b44..08617b6 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java
@@ -21,21 +21,16 @@
import static com.google.gerrit.reviewdb.client.Change.Status.SUBMITTED;
import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.ListenableFuture;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.query.AndPredicate;
-import com.google.gerrit.server.query.OperatorPredicate;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.RewritePredicate;
import com.google.gerrit.server.query.change.AndSource;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.query.change.ChangeDataSource;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.OrSource;
import com.google.gerrit.server.query.change.SqlRewriterImpl;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
import junit.framework.TestCase;
@@ -44,127 +39,7 @@
@SuppressWarnings("unchecked")
public class IndexRewriteTest extends TestCase {
- private static Schema<ChangeData> V1 = new Schema<ChangeData>(
- 1, false, ImmutableList.<FieldDef<ChangeData, ?>> of(
- ChangeField.STATUS));
-
- private static Schema<ChangeData> V2 = new Schema<ChangeData>(
- 2, false, ImmutableList.of(
- ChangeField.STATUS,
- ChangeField.FILE));
-
- private static class DummyIndex implements ChangeIndex {
- private final Schema<ChangeData> schema;
-
- private DummyIndex(Schema<ChangeData> schema) {
- this.schema = schema;
- }
-
- @Override
- public ListenableFuture<Void> insert(ChangeData cd) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public ListenableFuture<Void> replace(ChangeData cd) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public ListenableFuture<Void> delete(ChangeData cd) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void deleteAll() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public ChangeDataSource getSource(Predicate<ChangeData> p, int limit)
- throws QueryParseException {
- return new Source(p);
- }
-
- @Override
- public Schema<ChangeData> getSchema() {
- return schema;
- }
-
- @Override
- public void close() {
- }
-
- @Override
- public void markReady(boolean ready) {
- throw new UnsupportedOperationException();
- }
- }
-
- private static class Source implements ChangeDataSource {
- private final Predicate<ChangeData> p;
-
- Source(Predicate<ChangeData> p) {
- this.p = p;
- }
-
- @Override
- public int getCardinality() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean hasChange() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public ResultSet<ChangeData> read() throws OrmException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String toString() {
- return p.toString();
- }
- }
-
- public class QueryBuilder extends ChangeQueryBuilder {
- QueryBuilder() {
- super(
- new QueryBuilder.Definition<ChangeData, QueryBuilder>(
- QueryBuilder.class),
- new ChangeQueryBuilder.Arguments(null, null, null, null, null, null,
- null, null, null, null, null, indexes),
- null);
- }
-
- @Operator
- public Predicate<ChangeData> foo(String value) {
- return predicate("foo", value);
- }
-
- @Operator
- public Predicate<ChangeData> bar(String value) {
- return predicate("bar", value);
- }
-
- private Predicate<ChangeData> predicate(String name, String value) {
- return new OperatorPredicate<ChangeData>(name, value) {
- @Override
- public boolean match(ChangeData object) throws OrmException {
- return false;
- }
-
- @Override
- public int getCost() {
- return 0;
- }
- };
- }
- }
-
- private DummyIndex index;
+ private FakeIndex index;
private IndexCollection indexes;
private ChangeQueryBuilder queryBuilder;
private IndexRewriteImpl rewrite;
@@ -172,10 +47,10 @@
@Override
public void setUp() throws Exception {
super.setUp();
- index = new DummyIndex(V2);
+ index = new FakeIndex(FakeIndex.V2);
indexes = new IndexCollection();
indexes.setSearchIndex(index);
- queryBuilder = new QueryBuilder();
+ queryBuilder = new FakeQueryBuilder(indexes);
rewrite = new IndexRewriteImpl(
indexes,
null,
@@ -302,7 +177,7 @@
Predicate<ChangeData> in = parse("status:merged file:a");
assertEquals(query(in), rewrite(in));
- indexes.setSearchIndex(new DummyIndex(V1));
+ indexes.setSearchIndex(new FakeIndex(FakeIndex.V1));
Predicate<ChangeData> out = rewrite(in);
assertTrue(out instanceof AndPredicate);
assertEquals(ImmutableList.of(
@@ -339,7 +214,7 @@
private IndexedChangeQuery query(Predicate<ChangeData> p, int limit)
throws QueryParseException {
- return new IndexedChangeQuery(index, p, limit);
+ return new IndexedChangeQuery(null, index, p, limit);
}
private Set<Change.Status> status(String query) throws QueryParseException {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexedChangeQueryTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexedChangeQueryTest.java
new file mode 100644
index 0000000..ba1f53d
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexedChangeQueryTest.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.package com.google.gerrit.server.git;
+
+package com.google.gerrit.server.index;
+
+import static com.google.gerrit.server.index.IndexedChangeQuery.replaceSortKeyPredicates;
+
+import com.google.gerrit.server.query.Predicate;
+import com.google.gerrit.server.query.QueryParseException;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.ChangeQueryBuilder;
+
+import junit.framework.TestCase;
+
+public class IndexedChangeQueryTest extends TestCase {
+ private FakeIndex index;
+ private ChangeQueryBuilder queryBuilder;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ index = new FakeIndex(FakeIndex.V2);
+ IndexCollection indexes = new IndexCollection();
+ indexes.setSearchIndex(index);
+ queryBuilder = new FakeQueryBuilder(indexes);
+ }
+
+ public void testReplaceSortKeyPredicate_NoSortKey() throws Exception {
+ Predicate<ChangeData> p = parse("foo:a bar:b OR (foo:b bar:a)");
+ assertSame(p, replaceSortKeyPredicates(p, "1234"));
+ }
+
+ public void testReplaceSortKeyPredicate_TopLevelSortKey() throws Exception {
+ Predicate<ChangeData> p;
+ p = parse("foo:a bar:b sortkey_before:1234 OR (foo:b bar:a)");
+ assertEquals(parse("foo:a bar:b sortkey_before:5678 OR (foo:b bar:a)"),
+ replaceSortKeyPredicates(p, "5678"));
+ p = parse("foo:a bar:b sortkey_after:1234 OR (foo:b bar:a)");
+ assertEquals(parse("foo:a bar:b sortkey_after:5678 OR (foo:b bar:a)"),
+ replaceSortKeyPredicates(p, "5678"));
+ }
+
+ public void testReplaceSortKeyPredicate_NestedSortKey() throws Exception {
+ Predicate<ChangeData> p;
+ p = parse("foo:a bar:b OR (foo:b bar:a AND sortkey_before:1234)");
+ assertEquals(parse("foo:a bar:b OR (foo:b bar:a sortkey_before:5678)"),
+ replaceSortKeyPredicates(p, "5678"));
+ p = parse("foo:a bar:b OR (foo:b bar:a AND sortkey_after:1234)");
+ assertEquals(parse("foo:a bar:b OR (foo:b bar:a sortkey_after:5678)"),
+ replaceSortKeyPredicates(p, "5678"));
+ }
+
+ private Predicate<ChangeData> parse(String query) throws QueryParseException {
+ return queryBuilder.parse(query);
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
index 968d661..8f4e058 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
@@ -20,59 +20,63 @@
import static com.google.gerrit.common.data.Permission.PUSH;
import static com.google.gerrit.common.data.Permission.READ;
import static com.google.gerrit.common.data.Permission.SUBMIT;
+import static com.google.gerrit.server.project.Util.ANONYMOUS;
+import static com.google.gerrit.server.project.Util.REGISTERED;
+import static com.google.gerrit.server.project.Util.ADMIN;
+import static com.google.gerrit.server.project.Util.DEVS;
+import static com.google.gerrit.server.project.Util.grant;
+import static com.google.gerrit.server.project.Util.doNotInherit;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.collect.Lists;
import com.google.gerrit.common.data.Capable;
-import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.rules.PrologEnvironment;
-import com.google.gerrit.rules.RulesCache;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.CapabilityControl;
-import com.google.gerrit.server.account.GroupMembership;
-import com.google.gerrit.server.account.ListGroupMembership;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.FactoryModule;
-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.Guice;
-import com.google.inject.Injector;
import junit.framework.TestCase;
-import org.eclipse.jgit.lib.Config;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
public class RefControlTest extends TestCase {
- public void testOwnerProject() {
- grant(local, OWNER, admin, "refs/*");
+ private static void assertOwner(String ref, ProjectControl u) {
+ assertTrue("OWN " + ref, u.controlForRef(ref).isOwner());
+ }
- ProjectControl uBlah = user(devs);
- ProjectControl uAdmin = user(devs, admin);
+ private static void assertNotOwner(String ref, ProjectControl u) {
+ assertFalse("NOT OWN " + ref, u.controlForRef(ref).isOwner());
+ }
+
+ private final AccountGroup.UUID fixers = new AccountGroup.UUID("test.fixers");
+ private Project.NameKey localKey = new Project.NameKey("local");
+ private ProjectConfig local;
+ private final Util util;
+
+ public RefControlTest() {
+ util = new Util();
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ local = new ProjectConfig(localKey);
+ local.createInMemory();
+ util.add(local);
+ }
+
+ public void testOwnerProject() {
+ grant(local, OWNER, ADMIN, "refs/*");
+
+ ProjectControl uBlah = util.user(local, DEVS);
+ ProjectControl uAdmin = util.user(local, DEVS, ADMIN);
assertFalse("not owner", uBlah.isOwner());
assertTrue("is owner", uAdmin.isOwner());
}
public void testBranchDelegation1() {
- grant(local, OWNER, admin, "refs/*");
- grant(local, OWNER, devs, "refs/heads/x/*");
+ grant(local, OWNER, ADMIN, "refs/*");
+ grant(local, OWNER, DEVS, "refs/heads/x/*");
- ProjectControl uDev = user(devs);
+ ProjectControl uDev = util.user(local, DEVS);
assertFalse("not owner", uDev.isOwner());
assertTrue("owns ref", uDev.isOwnerAnyRef());
@@ -85,12 +89,12 @@
}
public void testBranchDelegation2() {
- grant(local, OWNER, admin, "refs/*");
- grant(local, OWNER, devs, "refs/heads/x/*");
+ grant(local, OWNER, ADMIN, "refs/*");
+ grant(local, OWNER, DEVS, "refs/heads/x/*");
grant(local, OWNER, fixers, "refs/heads/x/y/*");
doNotInherit(local, OWNER, "refs/heads/x/y/*");
- ProjectControl uDev = user(devs);
+ ProjectControl uDev = util.user(local, DEVS);
assertFalse("not owner", uDev.isOwner());
assertTrue("owns ref", uDev.isOwnerAnyRef());
@@ -100,7 +104,7 @@
assertNotOwner("refs/*", uDev);
assertNotOwner("refs/heads/master", uDev);
- ProjectControl uFix = user(fixers);
+ ProjectControl uFix = util.user(local, fixers);
assertFalse("not owner", uFix.isOwner());
assertTrue("owns ref", uFix.isOwnerAnyRef());
@@ -113,13 +117,13 @@
}
public void testInheritRead_SingleBranchDeniesUpload() {
- grant(parent, READ, registered, "refs/*");
- grant(parent, PUSH, registered, "refs/for/refs/*");
- grant(local, READ, registered, "refs/heads/foobar");
+ grant(util.getParentConfig(), READ, REGISTERED, "refs/*");
+ grant(util.getParentConfig(), PUSH, REGISTERED, "refs/for/refs/*");
+ grant(local, READ, REGISTERED, "refs/heads/foobar");
doNotInherit(local, READ, "refs/heads/foobar");
doNotInherit(local, PUSH, "refs/for/refs/heads/foobar");
- ProjectControl u = user();
+ ProjectControl u = util.user(local);
assertTrue("can upload", u.canPushToAtLeastOneRef() == Capable.OK);
assertTrue("can upload refs/heads/master", //
@@ -130,11 +134,11 @@
}
public void testInheritRead_SingleBranchDoesNotOverrideInherited() {
- grant(parent, READ, registered, "refs/*");
- grant(parent, PUSH, registered, "refs/for/refs/*");
- grant(local, READ, registered, "refs/heads/foobar");
+ grant(util.getParentConfig(), READ, REGISTERED, "refs/*");
+ grant(util.getParentConfig(), PUSH, REGISTERED, "refs/for/refs/*");
+ grant(local, READ, REGISTERED, "refs/heads/foobar");
- ProjectControl u = user();
+ ProjectControl u = util.user(local);
assertTrue("can upload", u.canPushToAtLeastOneRef() == Capable.OK);
assertTrue("can upload refs/heads/master", //
@@ -145,30 +149,30 @@
}
public void testInheritDuplicateSections() {
- grant(parent, READ, admin, "refs/*");
- grant(local, READ, devs, "refs/heads/*");
- local.getProject().setParentName(parent.getProject().getName());
- assertTrue("a can read", user("a", admin).isVisible());
+ grant(util.getParentConfig(), READ, ADMIN, "refs/*");
+ grant(local, READ, DEVS, "refs/heads/*");
+ local.getProject().setParentName(util.getParentConfig().getProject().getName());
+ assertTrue("a can read", util.user(local, "a", ADMIN).isVisible());
local = new ProjectConfig(new Project.NameKey("local"));
local.createInMemory();
- grant(local, READ, devs, "refs/*");
- assertTrue("d can read", user("d", devs).isVisible());
+ grant(local, READ, DEVS, "refs/*");
+ assertTrue("d can read", util.user(local, "d", DEVS).isVisible());
}
public void testInheritRead_OverrideWithDeny() {
- grant(parent, READ, registered, "refs/*");
- grant(local, READ, registered, "refs/*").setDeny();
+ grant(util.getParentConfig(), READ, REGISTERED, "refs/*");
+ grant(local, READ, REGISTERED, "refs/*").setDeny();
- ProjectControl u = user();
+ ProjectControl u = util.user(local);
assertFalse("can't read", u.isVisible());
}
public void testInheritRead_AppendWithDenyOfRef() {
- grant(parent, READ, registered, "refs/*");
- grant(local, READ, registered, "refs/heads/*").setDeny();
+ grant(util.getParentConfig(), READ, REGISTERED, "refs/*");
+ grant(local, READ, REGISTERED, "refs/heads/*").setDeny();
- ProjectControl u = user();
+ ProjectControl u = util.user(local);
assertTrue("can read", u.isVisible());
assertTrue("can read", u.controlForRef("refs/master").isVisible());
assertTrue("can read", u.controlForRef("refs/tags/foobar").isVisible());
@@ -176,11 +180,11 @@
}
public void testInheritRead_OverridesAndDeniesOfRef() {
- grant(parent, READ, registered, "refs/*");
- grant(local, READ, registered, "refs/*").setDeny();
- grant(local, READ, registered, "refs/heads/*");
+ grant(util.getParentConfig(), READ, REGISTERED, "refs/*");
+ grant(local, READ, REGISTERED, "refs/*").setDeny();
+ grant(local, READ, REGISTERED, "refs/heads/*");
- ProjectControl u = user();
+ ProjectControl u = util.user(local);
assertTrue("can read", u.isVisible());
assertFalse("can't read", u.controlForRef("refs/foobar").isVisible());
assertFalse("can't read", u.controlForRef("refs/tags/foobar").isVisible());
@@ -188,65 +192,65 @@
}
public void testInheritSubmit_OverridesAndDeniesOfRef() {
- grant(parent, SUBMIT, registered, "refs/*");
- grant(local, SUBMIT, registered, "refs/*").setDeny();
- grant(local, SUBMIT, registered, "refs/heads/*");
+ grant(util.getParentConfig(), SUBMIT, REGISTERED, "refs/*");
+ grant(local, SUBMIT, REGISTERED, "refs/*").setDeny();
+ grant(local, SUBMIT, REGISTERED, "refs/heads/*");
- ProjectControl u = user();
+ ProjectControl u = util.user(local);
assertFalse("can't submit", u.controlForRef("refs/foobar").canSubmit());
assertFalse("can't submit", u.controlForRef("refs/tags/foobar").canSubmit());
assertTrue("can submit", u.controlForRef("refs/heads/foobar").canSubmit());
}
public void testCannotUploadToAnyRef() {
- grant(parent, READ, registered, "refs/*");
- grant(local, READ, devs, "refs/heads/*");
- grant(local, PUSH, devs, "refs/for/refs/heads/*");
+ grant(util.getParentConfig(), READ, REGISTERED, "refs/*");
+ grant(local, READ, DEVS, "refs/heads/*");
+ grant(local, PUSH, DEVS, "refs/for/refs/heads/*");
- ProjectControl u = user();
+ ProjectControl u = util.user(local);
assertFalse("cannot upload", u.canPushToAtLeastOneRef() == Capable.OK);
assertFalse("cannot upload refs/heads/master", //
u.controlForRef("refs/heads/master").canUpload());
}
public void testUsernamePatternNonRegex() {
- grant(local, READ, devs, "refs/sb/${username}/heads/*");
+ grant(local, READ, DEVS, "refs/sb/${username}/heads/*");
- ProjectControl u = user("u", devs), d = user("d", devs);
+ ProjectControl u = util.user(local, "u", DEVS), d = util.user(local, "d", DEVS);
assertFalse("u can't read", u.controlForRef("refs/sb/d/heads/foobar").isVisible());
assertTrue("d can read", d.controlForRef("refs/sb/d/heads/foobar").isVisible());
}
public void testUsernamePatternWithRegex() {
- grant(local, READ, devs, "^refs/sb/${username}/heads/.*");
+ grant(local, READ, DEVS, "^refs/sb/${username}/heads/.*");
- ProjectControl u = user("d.v", devs), d = user("dev", devs);
+ ProjectControl u = util.user(local, "d.v", DEVS), d = util.user(local, "dev", DEVS);
assertFalse("u can't read", u.controlForRef("refs/sb/dev/heads/foobar").isVisible());
assertTrue("d can read", d.controlForRef("refs/sb/dev/heads/foobar").isVisible());
}
public void testSortWithRegex() {
- grant(local, READ, devs, "^refs/heads/.*");
- grant(parent, READ, anonymous, "^refs/heads/.*-QA-.*");
+ grant(local, READ, DEVS, "^refs/heads/.*");
+ grant(util.getParentConfig(), READ, ANONYMOUS, "^refs/heads/.*-QA-.*");
- ProjectControl u = user(devs), d = user(devs);
+ ProjectControl u = util.user(local, DEVS), d = util.user(local, DEVS);
assertTrue("u can read", u.controlForRef("refs/heads/foo-QA-bar").isVisible());
assertTrue("d can read", d.controlForRef("refs/heads/foo-QA-bar").isVisible());
}
public void testBlockRule_ParentBlocksChild() {
- grant(local, PUSH, devs, "refs/tags/*");
- grant(parent, PUSH, anonymous, "refs/tags/*").setBlock();
+ grant(local, PUSH, DEVS, "refs/tags/*");
+ grant(util.getParentConfig(), PUSH, ANONYMOUS, "refs/tags/*").setBlock();
- ProjectControl u = user(devs);
+ ProjectControl u = util.user(local, DEVS);
assertFalse("u can't force update tag", u.controlForRef("refs/tags/V10").canForceUpdate());
}
public void testBlockLabelRange_ParentBlocksChild() {
- grant(local, LABEL + "Code-Review", -2, +2, devs, "refs/heads/*");
- grant(parent, LABEL + "Code-Review", -2, +2, devs, "refs/heads/*").setBlock();
+ grant(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
+ grant(util.getParentConfig(), LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*").setBlock();
- ProjectControl u = user(devs);
+ ProjectControl u = util.user(local, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertTrue("u can vote -1", range.contains(-1));
@@ -256,338 +260,140 @@
}
public void testUnblockNoForce() {
- grant(local, PUSH, anonymous, "refs/heads/*").setBlock();
- grant(local, PUSH, devs, "refs/heads/*");
+ grant(local, PUSH, ANONYMOUS, "refs/heads/*").setBlock();
+ grant(local, PUSH, DEVS, "refs/heads/*");
- ProjectControl u = user(devs);
+ ProjectControl u = util.user(local, DEVS);
assertTrue("u can push", u.controlForRef("refs/heads/master").canUpdate());
}
public void testUnblockForce() {
- PermissionRule r = grant(local, PUSH, anonymous, "refs/heads/*");
+ PermissionRule r = grant(local, PUSH, ANONYMOUS, "refs/heads/*");
r.setBlock();
r.setForce(true);
- grant(local, PUSH, devs, "refs/heads/*").setForce(true);
+ grant(local, PUSH, DEVS, "refs/heads/*").setForce(true);
- ProjectControl u = user(devs);
+ ProjectControl u = util.user(local, DEVS);
assertTrue("u can force push", u.controlForRef("refs/heads/master").canForceUpdate());
}
public void testUnblockForceWithAllowNoForce_NotPossible() {
- PermissionRule r = grant(local, PUSH, anonymous, "refs/heads/*");
+ PermissionRule r = grant(local, PUSH, ANONYMOUS, "refs/heads/*");
r.setBlock();
r.setForce(true);
- grant(local, PUSH, devs, "refs/heads/*");
+ grant(local, PUSH, DEVS, "refs/heads/*");
- ProjectControl u = user(devs);
+ ProjectControl u = util.user(local, DEVS);
assertFalse("u can't force push", u.controlForRef("refs/heads/master").canForceUpdate());
}
public void testUnblockMoreSpecificRef_Fails() {
- grant(local, PUSH, anonymous, "refs/heads/*").setBlock();
- grant(local, PUSH, devs, "refs/heads/master");
+ grant(local, PUSH, ANONYMOUS, "refs/heads/*").setBlock();
+ grant(local, PUSH, DEVS, "refs/heads/master");
- ProjectControl u = user(devs);
+ ProjectControl u = util.user(local, DEVS);
assertFalse("u can't push", u.controlForRef("refs/heads/master").canUpdate());
}
public void testUnblockLargerScope_Fails() {
- grant(local, PUSH, anonymous, "refs/heads/master").setBlock();
- grant(local, PUSH, devs, "refs/heads/*");
+ grant(local, PUSH, ANONYMOUS, "refs/heads/master").setBlock();
+ grant(local, PUSH, DEVS, "refs/heads/*");
- ProjectControl u = user(devs);
+ ProjectControl u = util.user(local, DEVS);
assertFalse("u can't push", u.controlForRef("refs/heads/master").canUpdate());
}
public void testUnblockInLocal_Fails() {
- grant(parent, PUSH, anonymous, "refs/heads/*").setBlock();
+ grant(util.getParentConfig(), PUSH, ANONYMOUS, "refs/heads/*").setBlock();
grant(local, PUSH, fixers, "refs/heads/*");
- ProjectControl f = user(fixers);
+ ProjectControl f = util.user(local, fixers);
assertFalse("u can't push", f.controlForRef("refs/heads/master").canUpdate());
}
public void testUnblockInParentBlockInLocal() {
- grant(parent, PUSH, anonymous, "refs/heads/*").setBlock();
- grant(parent, PUSH, devs, "refs/heads/*");
- grant(local, PUSH, devs, "refs/heads/*").setBlock();
+ grant(util.getParentConfig(), PUSH, ANONYMOUS, "refs/heads/*").setBlock();
+ grant(util.getParentConfig(), PUSH, DEVS, "refs/heads/*");
+ grant(local, PUSH, DEVS, "refs/heads/*").setBlock();
- ProjectControl d = user(devs);
+ ProjectControl d = util.user(local, DEVS);
assertFalse("u can't push", d.controlForRef("refs/heads/master").canUpdate());
}
- public void testUnblockVisibilityByRegisteredUsers() {
- grant(local, READ, anonymous, "refs/heads/*").setBlock();
- grant(local, READ, registered, "refs/heads/*");
+ public void testUnblockVisibilityByREGISTEREDUsers() {
+ grant(local, READ, ANONYMOUS, "refs/heads/*").setBlock();
+ grant(local, READ, REGISTERED, "refs/heads/*");
- ProjectControl u = user(registered);
+ ProjectControl u = util.user(local, REGISTERED);
assertTrue("u can read", u.controlForRef("refs/heads/master").isVisibleByRegisteredUsers());
}
public void testUnblockInLocalVisibilityByRegisteredUsers_Fails() {
- grant(parent, READ, anonymous, "refs/heads/*").setBlock();
- grant(local, READ, registered, "refs/heads/*");
+ grant(util.getParentConfig(), READ, ANONYMOUS, "refs/heads/*").setBlock();
+ grant(local, READ, REGISTERED, "refs/heads/*");
- ProjectControl u = user(registered);
+ ProjectControl u = util.user(local, REGISTERED);
assertFalse("u can't read", u.controlForRef("refs/heads/master").isVisibleByRegisteredUsers());
}
public void testUnblockForceEditTopicName() {
- grant(local, EDIT_TOPIC_NAME, anonymous, "refs/heads/*").setBlock();
- grant(local, EDIT_TOPIC_NAME, devs, "refs/heads/*").setForce(true);
+ grant(local, EDIT_TOPIC_NAME, ANONYMOUS, "refs/heads/*").setBlock();
+ grant(local, EDIT_TOPIC_NAME, DEVS, "refs/heads/*").setForce(true);
- ProjectControl u = user(devs);
- assertTrue("u can edit topic name", u.controlForRef("refs/heads/master").canForceEditTopicName());
+ ProjectControl u = util.user(local, DEVS);
+ assertTrue("u can edit topic name", u.controlForRef("refs/heads/master")
+ .canForceEditTopicName());
}
public void testUnblockInLocalForceEditTopicName_Fails() {
- grant(parent, EDIT_TOPIC_NAME, anonymous, "refs/heads/*").setBlock();
- grant(local, EDIT_TOPIC_NAME, devs, "refs/heads/*").setForce(true);
+ grant(util.getParentConfig(), EDIT_TOPIC_NAME, ANONYMOUS, "refs/heads/*")
+ .setBlock();
+ grant(local, EDIT_TOPIC_NAME, DEVS, "refs/heads/*").setForce(true);
- ProjectControl u = user(registered);
- assertFalse("u can't edit topic name", u.controlForRef("refs/heads/master").canForceEditTopicName());
+ ProjectControl u = util.user(local, REGISTERED);
+ assertFalse("u can't edit topic name", u.controlForRef("refs/heads/master")
+ .canForceEditTopicName());
}
public void testUnblockRange() {
- grant(local, LABEL + "Code-Review", -1, +1, anonymous, "refs/heads/*").setBlock();
- grant(local, LABEL + "Code-Review", -2, +2, devs, "refs/heads/*");
+ grant(local, LABEL + "Code-Review", -1, +1, ANONYMOUS, "refs/heads/*").setBlock();
+ grant(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
- ProjectControl u = user(devs);
+ ProjectControl u = util.user(local, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertTrue("u can vote -2", range.contains(-2));
assertTrue("u can vote +2", range.contains(2));
}
public void testUnblockRangeOnMoreSpecificRef_Fails() {
- grant(local, LABEL + "Code-Review", -1, +1, anonymous, "refs/heads/*").setBlock();
- grant(local, LABEL + "Code-Review", -2, +2, devs, "refs/heads/master");
+ grant(local, LABEL + "Code-Review", -1, +1, ANONYMOUS, "refs/heads/*").setBlock();
+ grant(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/master");
- ProjectControl u = user(devs);
+ ProjectControl u = util.user(local, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertFalse("u can't vote -2", range.contains(-2));
assertFalse("u can't vote +2", range.contains(-2));
}
public void testUnblockRangeOnLargerScope_Fails() {
- grant(local, LABEL + "Code-Review", -1, +1, anonymous, "refs/heads/master").setBlock();
- grant(local, LABEL + "Code-Review", -2, +2, devs, "refs/heads/*");
+ grant(local, LABEL + "Code-Review", -1, +1, ANONYMOUS, "refs/heads/master").setBlock();
+ grant(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
- ProjectControl u = user(devs);
+ ProjectControl u = util.user(local, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertFalse("u can't vote -2", range.contains(-2));
assertFalse("u can't vote +2", range.contains(-2));
}
public void testUnblockInLocalRange_Fails() {
- grant(parent, LABEL + "Code-Review", -1, 1, anonymous, "refs/heads/*").setBlock();
- grant(local, LABEL + "Code-Review", -2, +2, devs, "refs/heads/*");
+ grant(util.getParentConfig(), LABEL + "Code-Review", -1, 1, ANONYMOUS,
+ "refs/heads/*").setBlock();
+ grant(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
- ProjectControl u = user(devs);
- PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
+ ProjectControl u = util.user(local, DEVS);
+ PermissionRange range =
+ u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertFalse("u can't vote -2", range.contains(-2));
assertFalse("u can't vote 2", range.contains(2));
}
- // -----------------------------------------------------------------------
-
- private final Map<Project.NameKey, ProjectState> all;
- private final AllProjectsName allProjectsName = new AllProjectsName("parent");
- private final ProjectCache projectCache;
-
- private ProjectConfig local;
- private ProjectConfig parent;
- private PermissionCollection.Factory sectionSorter;
-
- private final AccountGroup.UUID admin = new AccountGroup.UUID("test.admin");
- private final AccountGroup.UUID anonymous = AccountGroup.ANONYMOUS_USERS;
- private final AccountGroup.UUID registered = AccountGroup.REGISTERED_USERS;
-
- private final AccountGroup.UUID devs = new AccountGroup.UUID("test.devs");
- private final AccountGroup.UUID fixers = new AccountGroup.UUID("test.fixers");
-
- private final CapabilityControl.Factory capabilityControlFactory;
-
- public RefControlTest() {
- all = new HashMap<Project.NameKey, ProjectState>();
- projectCache = new ProjectCache() {
- @Override
- public ProjectState getAllProjects() {
- return get(allProjectsName);
- }
-
- @Override
- public ProjectState get(Project.NameKey projectName) {
- return all.get(projectName);
- }
-
- @Override
- public ProjectState checkedGet(Project.NameKey projectName) {
- return get(projectName);
- }
-
- @Override
- public void evict(Project p) {
- }
-
- @Override
- public void evict(Project.NameKey p) {
- }
-
- @Override
- public void remove(Project p) {
- }
-
- @Override
- public Iterable<Project.NameKey> all() {
- return Collections.emptySet();
- }
-
- @Override
- public Iterable<Project.NameKey> byName(String prefix) {
- return Collections.emptySet();
- }
-
- @Override
- public void onCreateProject(Project.NameKey newProjectName) {
- }
-
- @Override
- public Set<AccountGroup.UUID> guessRelevantGroupUUIDs() {
- return Collections.emptySet();
- }
- };
-
- Injector injector = Guice.createInjector(new FactoryModule() {
- @Override
- protected void configure() {
- bind(Config.class)
- .annotatedWith(GerritServerConfig.class)
- .toInstance(new Config());
-
- factory(CapabilityControl.Factory.class);
- bind(ProjectCache.class).toInstance(projectCache);
- }
- });
- capabilityControlFactory = injector.getInstance(CapabilityControl.Factory.class);
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
-
- parent = new ProjectConfig(new Project.NameKey("parent"));
- parent.createInMemory();
-
- local = new ProjectConfig(new Project.NameKey("local"));
- local.createInMemory();
-
- Cache<SectionSortCache.EntryKey, SectionSortCache.EntryVal> c =
- CacheBuilder.newBuilder().build();
- sectionSorter = new PermissionCollection.Factory(new SectionSortCache(c));
- }
-
- private static void assertOwner(String ref, ProjectControl u) {
- assertTrue("OWN " + ref, u.controlForRef(ref).isOwner());
- }
-
- private static void assertNotOwner(String ref, ProjectControl u) {
- assertFalse("NOT OWN " + ref, u.controlForRef(ref).isOwner());
- }
-
- private PermissionRule grant(ProjectConfig project, String permissionName,
- AccountGroup.UUID group, String ref) {
- return grant(project, permissionName, newRule(project, group), ref);
- }
-
- private PermissionRule grant(ProjectConfig project, String permissionName,
- int min, int max, AccountGroup.UUID group, String ref) {
- PermissionRule rule = newRule(project, group);
- rule.setMin(min);
- rule.setMax(max);
- return grant(project, permissionName, rule, ref);
- }
-
-
- private PermissionRule grant(ProjectConfig project, String permissionName,
- PermissionRule rule, String ref) {
- project.getAccessSection(ref, true) //
- .getPermission(permissionName, true) //
- .add(rule);
- return rule;
- }
-
- private void doNotInherit(ProjectConfig project, String permissionName,
- String ref) {
- project.getAccessSection(ref, true) //
- .getPermission(permissionName, true) //
- .setExclusiveGroup(true);
- }
-
- private PermissionRule newRule(ProjectConfig project, AccountGroup.UUID groupUUID) {
- GroupReference group = new GroupReference(groupUUID, groupUUID.get());
- group = project.resolve(group);
-
- return new PermissionRule(group);
- }
-
- private ProjectControl user(AccountGroup.UUID... memberOf) {
- return user(null, memberOf);
- }
-
- private ProjectControl user(String name, AccountGroup.UUID... memberOf) {
- String canonicalWebUrl = "http://localhost";
-
- return new ProjectControl(Collections.<AccountGroup.UUID> emptySet(),
- Collections.<AccountGroup.UUID> emptySet(), projectCache,
- sectionSorter,
- canonicalWebUrl, new MockUser(name, memberOf),
- newProjectState());
- }
-
- private ProjectState newProjectState() {
- PrologEnvironment.Factory envFactory = null;
- GitRepositoryManager mgr = null;
- ProjectControl.AssistedFactory projectControlFactory = null;
- RulesCache rulesCache = null;
- all.put(local.getProject().getNameKey(), new ProjectState(
- null, projectCache, allProjectsName, projectControlFactory,
- envFactory, mgr, rulesCache, null, local));
- all.put(parent.getProject().getNameKey(), new ProjectState(
- null, projectCache, allProjectsName, projectControlFactory,
- envFactory, mgr, rulesCache, null, parent));
- return all.get(local.getProject().getNameKey());
- }
-
- private class MockUser extends CurrentUser {
- private final String username;
- private final GroupMembership groups;
-
- MockUser(String name, AccountGroup.UUID[] groupId) {
- super(RefControlTest.this.capabilityControlFactory);
- username = name;
- ArrayList<AccountGroup.UUID> groupIds = Lists.newArrayList(groupId);
- groupIds.add(registered);
- groupIds.add(anonymous);
- groups = new ListGroupMembership(groupIds);
- }
-
- @Override
- public GroupMembership getEffectiveGroups() {
- return groups;
- }
-
- @Override
- public String getUserName() {
- return username;
- }
-
- @Override
- public Set<Change.Id> getStarredChanges() {
- return Collections.emptySet();
- }
-
- @Override
- public Collection<AccountProjectWatch> getNotificationFilters() {
- return Collections.emptySet();
- }
- }
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
new file mode 100644
index 0000000..07f903c
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
@@ -0,0 +1,264 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.project;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.Lists;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.AccountProjectWatch;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.client.Project.NameKey;
+import com.google.gerrit.rules.PrologEnvironment;
+import com.google.gerrit.rules.RulesCache;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.CapabilityControl;
+import com.google.gerrit.server.account.GroupMembership;
+import com.google.gerrit.server.account.ListGroupMembership;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+import org.eclipse.jgit.lib.Config;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class Util {
+ public static AccountGroup.UUID ANONYMOUS = AccountGroup.ANONYMOUS_USERS;
+ public static AccountGroup.UUID REGISTERED = AccountGroup.REGISTERED_USERS;
+ public static AccountGroup.UUID ADMIN = new AccountGroup.UUID("test.admin");
+ public static AccountGroup.UUID DEVS = new AccountGroup.UUID("test.devs");
+
+ public static LabelType CR = category("Code-Review",
+ value(2, "Looks good to me, approved"),
+ value(1, "Looks good to me, but someone else must approve"),
+ value(0, "No score"),
+ value(-1, "I would prefer that you didn't submit this"),
+ value(-2, "Do not submit"));
+
+ public static LabelValue value(int value, String text) {
+ return new LabelValue((short) value, text);
+ }
+
+ public static LabelType category(String name, LabelValue... values) {
+ return new LabelType(name, Arrays.asList(values));
+ }
+
+ static public PermissionRule newRule(ProjectConfig project,
+ AccountGroup.UUID groupUUID) {
+ GroupReference group = new GroupReference(groupUUID, groupUUID.get());
+ group = project.resolve(group);
+
+ return new PermissionRule(group);
+ }
+
+ static public PermissionRule grant(ProjectConfig project,
+ String permissionName, int min, int max, AccountGroup.UUID group,
+ String ref) {
+ PermissionRule rule = newRule(project, group);
+ rule.setMin(min);
+ rule.setMax(max);
+ return grant(project, permissionName, rule, ref);
+ }
+
+ static public PermissionRule grant(ProjectConfig project,
+ String permissionName, AccountGroup.UUID group, String ref) {
+ return grant(project, permissionName, newRule(project, group), ref);
+ }
+
+ static public void doNotInherit(ProjectConfig project, String permissionName,
+ String ref) {
+ project.getAccessSection(ref, true) //
+ .getPermission(permissionName, true) //
+ .setExclusiveGroup(true);
+ }
+
+ static private PermissionRule grant(ProjectConfig project,
+ String permissionName, PermissionRule rule, String ref) {
+ project.getAccessSection(ref, true) //
+ .getPermission(permissionName, true) //
+ .add(rule);
+ return rule;
+ }
+
+ private final Map<Project.NameKey, ProjectState> all;
+ private final ProjectCache projectCache;
+ private final CapabilityControl.Factory capabilityControlFactory;
+ private final PermissionCollection.Factory sectionSorter;
+
+ private final AllProjectsName allProjectsName = new AllProjectsName("parent");
+ private final ProjectConfig parent = new ProjectConfig(allProjectsName);
+
+ public Util() {
+ all = new HashMap<Project.NameKey, ProjectState>();
+ parent.createInMemory();
+ parent.getLabelSections().put(CR.getName(), CR);
+
+ add(parent);
+
+ projectCache = new ProjectCache() {
+ @Override
+ public ProjectState getAllProjects() {
+ return get(allProjectsName);
+ }
+
+ @Override
+ public ProjectState get(Project.NameKey projectName) {
+ return all.get(projectName);
+ }
+
+ @Override
+ public void evict(Project p) {
+ }
+
+ @Override
+ public void remove(Project p) {
+ }
+
+ @Override
+ public Iterable<Project.NameKey> all() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Iterable<Project.NameKey> byName(String prefix) {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public void onCreateProject(Project.NameKey newProjectName) {
+ }
+
+ @Override
+ public Set<AccountGroup.UUID> guessRelevantGroupUUIDs() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public ProjectState checkedGet(NameKey projectName) throws IOException {
+ return all.get(projectName);
+ }
+
+ @Override
+ public void evict(NameKey p) {
+ }
+ };
+
+ Injector injector = Guice.createInjector(new FactoryModule() {
+ @Override
+ protected void configure() {
+ bind(Config.class).annotatedWith(GerritServerConfig.class).toInstance(
+ new Config());
+
+ factory(CapabilityControl.Factory.class);
+ bind(ProjectCache.class).toInstance(projectCache);
+ }
+ });
+
+ Cache<SectionSortCache.EntryKey, SectionSortCache.EntryVal> c =
+ CacheBuilder.newBuilder().build();
+ sectionSorter = new PermissionCollection.Factory(new SectionSortCache(c));
+ capabilityControlFactory =
+ injector.getInstance(CapabilityControl.Factory.class);
+ }
+
+ public ProjectConfig getParentConfig() {
+ return this.parent;
+ }
+
+ public void add(ProjectConfig pc) {
+ PrologEnvironment.Factory envFactory = null;
+ GitRepositoryManager mgr = null;
+ ProjectControl.AssistedFactory projectControlFactory = null;
+ RulesCache rulesCache = null;
+ SitePaths sitePaths = null;
+ List<CommentLinkInfo> commentLinks = null;
+
+ all.put(pc.getProject().getNameKey(), new ProjectState(sitePaths,
+ projectCache, allProjectsName, projectControlFactory, envFactory, mgr,
+ rulesCache, commentLinks, pc));
+ }
+
+ public ProjectControl user(ProjectConfig local, AccountGroup.UUID... memberOf) {
+ return user(local, null, memberOf);
+ }
+
+ public ProjectControl user(ProjectConfig local, String name,
+ AccountGroup.UUID... memberOf) {
+ String canonicalWebUrl = "http://localhost";
+
+ return new ProjectControl(Collections.<AccountGroup.UUID> emptySet(),
+ Collections.<AccountGroup.UUID> emptySet(), projectCache,
+ sectionSorter, canonicalWebUrl, new MockUser(name, memberOf),
+ newProjectState(local));
+ }
+
+ private ProjectState newProjectState(ProjectConfig local) {
+ add(local);
+ return all.get(local.getProject().getNameKey());
+ }
+
+ private class MockUser extends CurrentUser {
+ private final String username;
+ private final GroupMembership groups;
+
+ MockUser(String name, AccountGroup.UUID[] groupId) {
+ super(capabilityControlFactory);
+ username = name;
+ ArrayList<AccountGroup.UUID> groupIds = Lists.newArrayList(groupId);
+ groupIds.add(REGISTERED);
+ groupIds.add(ANONYMOUS);
+ groups = new ListGroupMembership(groupIds);
+ }
+
+ @Override
+ public GroupMembership getEffectiveGroups() {
+ return groups;
+ }
+
+ @Override
+ public String getUserName() {
+ return username;
+ }
+
+ @Override
+ public Set<Change.Id> getStarredChanges() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Collection<AccountProjectWatch> getNotificationFilters() {
+ return Collections.emptySet();
+ }
+ }
+}
diff --git a/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java b/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java
index 33150e8..2ddbecb 100644
--- a/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java
+++ b/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java
@@ -41,6 +41,7 @@
import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
+import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
@@ -204,7 +205,8 @@
if (!Sets.intersection(statuses, CLOSED_STATUSES).isEmpty()) {
indexes.add(closedIndex);
}
- return new QuerySource(indexes, QueryBuilder.toQuery(p), limit);
+ return new QuerySource(indexes, QueryBuilder.toQuery(schema, p), limit,
+ ChangeQueryBuilder.hasNonTrivialSortKeyAfter(schema, p));
}
private void commit(SolrServer server) throws IOException {
@@ -219,7 +221,9 @@
private final List<SolrServer> indexes;
private final SolrQuery query;
- public QuerySource(List<SolrServer> indexes, Query q, int limit) {
+ @SuppressWarnings("deprecation")
+ public QuerySource(List<SolrServer> indexes, Query q, int limit,
+ boolean reverse) {
this.indexes = indexes;
query = new SolrQuery(q.toString());
@@ -227,8 +231,8 @@
query.setParam("rows", Integer.toString(limit));
query.setFields(ID_FIELD);
query.setSort(
- ChangeField.UPDATED.getName(),
- SolrQuery.ORDER.desc);
+ ChangeField.SORTKEY.getName(),
+ !reverse ? SolrQuery.ORDER.desc : SolrQuery.ORDER.asc);
}
@Override
diff --git a/lib/asciidoctor/BUCK b/lib/asciidoctor/BUCK
index 25b9eb8..6a1fdd7 100644
--- a/lib/asciidoctor/BUCK
+++ b/lib/asciidoctor/BUCK
@@ -2,11 +2,21 @@
java_binary(
name = 'asciidoc',
- main_class = 'org.asciidoctor.cli.AsciidoctorInvoker',
- deps = [':asciidoctor'],
+ main_class = 'Main',
+ deps = [':main_lib'],
visibility = ['PUBLIC'],
)
+java_library(
+ name = 'main_lib',
+ srcs = ['java/Main.java'],
+ deps = [
+ ':asciidoctor',
+ ':jruby',
+ '//lib:args4j',
+ ],
+)
+
maven_jar(
name = 'asciidoctor',
id = 'org.asciidoctor:asciidoctor-java-integration:0.1.3',
@@ -14,19 +24,6 @@
license = 'Apache2.0',
visibility = [],
attach_source = False,
- deps = [
- ':jcommander',
- ':jruby',
- ],
-)
-
-maven_jar(
- name = 'jcommander',
- id = 'com.beust:jcommander:1.30',
- sha1 = 'c440b30a944ba199751551aee393f8aa03b3c327',
- license = 'Apache2.0',
- visibility = [],
- attach_source = False,
)
maven_jar(
diff --git a/lib/asciidoctor/java/Main.java b/lib/asciidoctor/java/Main.java
new file mode 100644
index 0000000..eb1ef33
--- /dev/null
+++ b/lib/asciidoctor/java/Main.java
@@ -0,0 +1,159 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.asciidoctor.Asciidoctor;
+import org.asciidoctor.AttributesBuilder;
+import org.asciidoctor.Options;
+import org.asciidoctor.OptionsBuilder;
+import org.asciidoctor.internal.JRubyAsciidoctor;
+
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.Option;
+
+public class Main {
+
+ private static final int BUFSIZ = 4096;
+ private static final String DOCTYPE = "article";
+ private static final String ERUBY = "erb";
+
+ @Option(name = "-b", usage = "set output format backend")
+ private String backend = "html5";
+
+ @Option(name = "-z", usage = "output zip file")
+ private String zipFile;
+
+ @Option(name = "--in-ext", usage = "extension for input files")
+ private String inExt = ".txt";
+
+ @Option(name = "--out-ext", usage = "extension for output files")
+ private String outExt = ".html";
+
+ @Option(name = "-a", usage =
+ "a list of attributes, in the form key or key=value pair")
+ private List<String> attributes = new ArrayList<String>();
+
+ @Argument(usage = "input files")
+ private List<String> inputFiles = new ArrayList<String>();
+
+ private String mapInFileToOutFile(String inFile) {
+ String basename = new File(inFile).getName();
+ if (basename.endsWith(inExt)) {
+ basename = basename.substring(0, basename.length() - inExt.length());
+ } else {
+ // Strip out the last extension
+ int pos = basename.lastIndexOf('.');
+ if (pos > 0) {
+ basename = basename.substring(0, pos);
+ }
+ }
+ return basename + outExt;
+ }
+
+ private Options createOptions(File tmpFile) {
+ OptionsBuilder optionsBuilder = OptionsBuilder.options();
+
+ optionsBuilder.backend(backend).docType(DOCTYPE).eruby(ERUBY);
+ // XXX(fishywang): ideally we should just output to a string and add the
+ // content into zip. But asciidoctor will actually ignore all attributes if
+ // not output to a file. So we *have* to output to a file then read the
+ // content of the file into zip.
+ optionsBuilder.toFile(tmpFile);
+
+ AttributesBuilder attributesBuilder = AttributesBuilder.attributes();
+ attributesBuilder.attributes(getAttributes());
+ optionsBuilder.attributes(attributesBuilder.get());
+
+ return optionsBuilder.get();
+ }
+
+ private Map<String, Object> getAttributes() {
+ Map<String, Object> attributeValues = new HashMap<String, Object>();
+
+ for (String attribute : attributes) {
+ int equalsIndex = attribute.indexOf('=');
+ if(equalsIndex > -1) {
+ String name = attribute.substring(0, equalsIndex);
+ String value = attribute.substring(equalsIndex + 1, attribute.length());
+
+ attributeValues.put(name, value);
+ } else {
+ attributeValues.put(attribute, "");
+ }
+ }
+
+ return attributeValues;
+ }
+
+ private void invoke(String... parameters) throws IOException {
+ CmdLineParser parser = new CmdLineParser(this);
+ try {
+ parser.parseArgument(parameters);
+ if (inputFiles.isEmpty()) {
+ throw new CmdLineException(parser,
+ "asciidoctor: FAILED: input file missing");
+ }
+ } catch (CmdLineException e) {
+ System.err.println(e.getMessage());
+ parser.printUsage(System.err);
+ System.exit(1);
+ return;
+ }
+
+ ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(zipFile));
+ byte[] buf = new byte[BUFSIZ];
+ for (String inputFile : inputFiles) {
+ File tmp = File.createTempFile("doc", ".html");
+ Options options = createOptions(tmp);
+ renderInput(options, inputFile);
+
+ FileInputStream input = new FileInputStream(tmp);
+ int len;
+ zip.putNextEntry(new ZipEntry(mapInFileToOutFile(inputFile)));
+ while ((len = input.read(buf)) > 0) {
+ zip.write(buf, 0, len);
+ }
+ input.close();
+ tmp.delete();
+ zip.closeEntry();
+ }
+ zip.close();
+ }
+
+ private void renderInput(Options options, String inputFile) {
+ Asciidoctor asciidoctor = JRubyAsciidoctor.create();
+ asciidoctor.renderFile(new File(inputFile), options);
+ }
+
+ public static void main(String[] args) {
+ try {
+ new Main().invoke(args);
+ } catch (IOException e) {
+ System.err.println(e.getMessage());
+ System.exit(1);
+ }
+ }
+}
diff --git a/lib/commons/BUCK b/lib/commons/BUCK
index fd066ef..6f412e4 100644
--- a/lib/commons/BUCK
+++ b/lib/commons/BUCK
@@ -73,7 +73,6 @@
id = 'commons-io:commons-io:1.4',
sha1 = 'a8762d07e76cfde2395257a5da47ba7c1dbd3dce',
license = 'Apache2.0',
- exclude = ['META-INF/MANIFEST.MF'],
)
maven_jar(
diff --git a/plugins/replication b/plugins/replication
index 0c5fef2..1308973 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 0c5fef2a8eda0e76677e5487d2fb8391e1cb126e
+Subproject commit 13089730a07f21eab4a1a302613f78729dc51e1b
diff --git a/tools/BUCK b/tools/BUCK
index 4323ff9..b03dcbc 100644
--- a/tools/BUCK
+++ b/tools/BUCK
@@ -1,22 +1,3 @@
-genrule(
- name = 'download',
- cmd = '$(exe :download_all)',
- deps = [':download_all'],
- out = '__fake.download__',
-)
-
-genrule(
- name = 'download_sources',
- cmd = '$(exe :download_all) --src',
- deps = [':download_all'],
- out = '__fake.download__',
-)
-
-python_binary(
- name = 'download_all',
- main = 'download_all.py',
-)
-
python_binary(
name = 'download_file',
main = 'download_file.py',
diff --git a/tools/eclipse/BUCK b/tools/eclipse/BUCK
index 4e752e9..874c3fc 100644
--- a/tools/eclipse/BUCK
+++ b/tools/eclipse/BUCK
@@ -1,27 +1,5 @@
include_defs('//tools/build.defs')
-genrule(
- name = 'eclipse',
- cmd = '',
- deps = [
- ':_classpath',
- ':_project',
- '//tools:buck.properties',
- ],
- out = '__fake.eclipse__',
-)
-
-genrule(
- name = 'eclipse_project',
- cmd = '',
- deps = [
- ':_classpath_nocompile',
- ':_project',
- '//tools:buck.properties',
- ],
- out = '__fake.eclipse__',
-)
-
java_library(
name = 'classpath',
deps = LIBS + PGMLIBS + [
@@ -35,37 +13,3 @@
'//lib/prolog:compiler_lib',
] + scan_plugins(),
)
-
-genrule(
- name = '_project',
- cmd = '$(exe :gen_project)',
- deps = [':gen_project'],
- out = '__fake.project__',
-)
-
-genrule(
- name = '_classpath',
- cmd = '$(exe :gen_classpath)',
- deps = [
- ':classpath',
- ':gen_classpath',
- ],
- out = '__fake.classpath__',
-)
-
-genrule(
- name = '_classpath_nocompile',
- cmd = '$(exe :gen_classpath)',
- deps = [':gen_classpath'],
- out = '__fake.classpath__',
-)
-
-python_binary(
- name = 'gen_classpath',
- main = 'gen_classpath.py',
-)
-
-python_binary(
- name = 'gen_project',
- main = 'gen_project.py',
-)
diff --git a/tools/eclipse/gen_classpath.py b/tools/eclipse/gen_classpath.py
deleted file mode 100755
index 241a876..0000000
--- a/tools/eclipse/gen_classpath.py
+++ /dev/null
@@ -1,118 +0,0 @@
-#!/usr/bin/python
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# TODO(sop): Remove hack after Buck supports Eclipse
-
-from os import path
-import re
-from subprocess import Popen, PIPE
-from sys import argv
-from xml.dom import minidom
-
-ROOT = path.abspath(__file__)
-for _ in range(0, 3):
- ROOT = path.dirname(ROOT)
-
-MAIN = ['//tools/eclipse:classpath']
-GWT = ['//gerrit-gwtui:ui_module']
-JRE = '/'.join([
- 'org.eclipse.jdt.launching.JRE_CONTAINER',
- 'org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType',
- 'JavaSE-1.6',
-])
-
-def query_classpath(targets):
- deps = []
- p = Popen(['buck', 'audit', 'classpath'] + targets, stdout = PIPE)
- for line in p.stdout:
- deps.append(line.strip())
- s = p.wait()
- if s != 0:
- exit(s)
- return deps
-
-def make_classpath():
- impl = minidom.getDOMImplementation()
- return impl.createDocument(None, 'classpath', None)
-
-doc = make_classpath()
-src = set()
-lib = set()
-gwt_src = set()
-gwt_lib = set()
-
-def classpathentry(kind, path, src = None):
- e = doc.createElement('classpathentry')
- e.setAttribute('kind', kind)
- e.setAttribute('path', path)
- if src:
- e.setAttribute('sourcepath', src)
- doc.documentElement.appendChild(e)
-
-java_library = re.compile(r'[^/]+/gen/(.*)/lib__[^/]+__output/[^/]+[.]jar$')
-for p in query_classpath(MAIN):
- if p.endswith('-src.jar'):
- # gwt_module() depends on -src.jar for Java to JavaScript compiles.
- gwt_lib.add(p)
- continue
-
- if p.startswith('buck-out/gen/lib/gwt/'):
- # gwt_module() depends on huge shaded GWT JARs that import
- # incorrect versions of classes for Gerrit. Collect into
- # a private grouping for later use.
- gwt_lib.add(p)
- continue
-
- m = java_library.match(p)
- if m:
- src.add(m.group(1))
- else:
- lib.add(p)
-
-for p in query_classpath(GWT):
- m = java_library.match(p)
- if m:
- gwt_src.add(m.group(1))
-
-for s in sorted(src):
- p = path.join(s, 'java')
- if path.exists(p):
- classpathentry('src', p)
- continue
-
- for env in ['main', 'test']:
- for type in ['java', 'resources']:
- p = path.join(s, 'src', env, type)
- if path.exists(p):
- classpathentry('src', p)
-
-for libs in [lib, gwt_lib]:
- for j in sorted(libs):
- s = None
- if j.endswith('.jar'):
- s = j[:-4] + '-src.jar'
- if not path.exists(s):
- s = None
- classpathentry('lib', j, s)
-
-for s in sorted(gwt_src):
- classpathentry('lib', path.join(ROOT, s, 'src', 'main', 'java'))
-
-classpathentry('con', JRE)
-classpathentry('output', 'buck-out/classes')
-
-p = path.join(ROOT, '.classpath')
-with open(p, 'w') as fd:
- doc.writexml(fd, addindent = ' ', newl = '\n', encoding='UTF-8')
diff --git a/tools/eclipse/gen_project.py b/tools/eclipse/gen_project.py
deleted file mode 100755
index d42d43c..0000000
--- a/tools/eclipse/gen_project.py
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/python
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# TODO(sop): Remove hack after Buck supports Eclipse
-
-from __future__ import print_function
-
-from os import path
-from sys import argv
-
-ROOT = path.abspath(__file__)
-for _ in range(0, 3):
- ROOT = path.dirname(ROOT)
-
-p = path.join(ROOT, '.project')
-with open(p, 'w') as fd:
- print("""\
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>gerrit</name>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>\
-""", file=fd)
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
new file mode 100755
index 0000000..884d0ff
--- /dev/null
+++ b/tools/eclipse/project.py
@@ -0,0 +1,157 @@
+#!/usr/bin/python
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# TODO(sop): Remove hack after Buck supports Eclipse
+
+from __future__ import print_function
+from optparse import OptionParser
+from os import path
+from subprocess import Popen, PIPE, CalledProcessError, check_call
+from xml.dom import minidom
+import re
+
+MAIN = ['//tools/eclipse:classpath']
+GWT = ['//gerrit-gwtui:ui_module']
+JRE = '/'.join([
+ 'org.eclipse.jdt.launching.JRE_CONTAINER',
+ 'org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType',
+ 'JavaSE-1.6',
+])
+
+ROOT = path.abspath(__file__)
+for _ in range(0, 3):
+ ROOT = path.dirname(ROOT)
+
+opts = OptionParser()
+opts.add_option('--src', action='store_true')
+args, _ = opts.parse_args()
+
+def gen_project():
+ p = path.join(ROOT, '.project')
+ with open(p, 'w') as fd:
+ print("""\
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>gerrit</name>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>\
+""", file=fd)
+
+def gen_classpath():
+ def query_classpath(targets):
+ deps = []
+ p = Popen(['buck', 'audit', 'classpath'] + targets, stdout=PIPE)
+ for line in p.stdout:
+ deps.append(line.strip())
+ s = p.wait()
+ if s != 0:
+ exit(s)
+ return deps
+
+ def make_classpath():
+ impl = minidom.getDOMImplementation()
+ return impl.createDocument(None, 'classpath', None)
+
+ def classpathentry(kind, path, src=None):
+ e = doc.createElement('classpathentry')
+ e.setAttribute('kind', kind)
+ e.setAttribute('path', path)
+ if src:
+ e.setAttribute('sourcepath', src)
+ doc.documentElement.appendChild(e)
+
+ doc = make_classpath()
+ src = set()
+ lib = set()
+ gwt_src = set()
+ gwt_lib = set()
+
+ java_library = re.compile(r'[^/]+/gen/(.*)/lib__[^/]+__output/[^/]+[.]jar$')
+ for p in query_classpath(MAIN):
+ if p.endswith('-src.jar'):
+ # gwt_module() depends on -src.jar for Java to JavaScript compiles.
+ gwt_lib.add(p)
+ continue
+
+ if p.startswith('buck-out/gen/lib/gwt/'):
+ # gwt_module() depends on huge shaded GWT JARs that import
+ # incorrect versions of classes for Gerrit. Collect into
+ # a private grouping for later use.
+ gwt_lib.add(p)
+ continue
+
+ m = java_library.match(p)
+ if m:
+ src.add(m.group(1))
+ else:
+ lib.add(p)
+
+ for p in query_classpath(GWT):
+ m = java_library.match(p)
+ if m:
+ gwt_src.add(m.group(1))
+
+ for s in sorted(src):
+ p = path.join(s, 'java')
+ if path.exists(p):
+ classpathentry('src', p)
+ continue
+
+ for env in ['main', 'test']:
+ for type in ['java', 'resources']:
+ p = path.join(s, 'src', env, type)
+ if path.exists(p):
+ classpathentry('src', p)
+
+ for libs in [lib, gwt_lib]:
+ for j in sorted(libs):
+ s = None
+ if j.endswith('.jar'):
+ s = j[:-4] + '-src.jar'
+ if not path.exists(s):
+ s = None
+ classpathentry('lib', j, s)
+
+ for s in sorted(gwt_src):
+ classpathentry('lib', path.join(ROOT, s, 'src', 'main', 'java'))
+
+ classpathentry('con', JRE)
+ classpathentry('output', 'buck-out/classes')
+
+ p = path.join(ROOT, '.classpath')
+ with open(p, 'w') as fd:
+ doc.writexml(fd, addindent=' ', newl='\n', encoding='UTF-8')
+
+if args.src:
+ try:
+ check_call([path.join(ROOT, 'tools', 'download_all.py'), '--src'])
+ except CalledProcessError as err:
+ exit(1)
+
+gen_project()
+gen_classpath()
+
+try:
+ targets = ['//tools:buck.properties'] + MAIN + GWT
+ check_call(['buck', 'build'] + targets)
+except CalledProcessError as err:
+ exit(1)
diff --git a/tools/maven/fake_pom.xml b/tools/maven/fake_pom.xml
new file mode 100644
index 0000000..d066a4a
--- /dev/null
+++ b/tools/maven/fake_pom.xml
@@ -0,0 +1,6 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>Gerrit-Code-Review-Maven</artifactId>
+ <version>1</version>
+</project>
diff --git a/tools/maven/mvn.py b/tools/maven/mvn.py
index dc098a3..fefc5b3 100644
--- a/tools/maven/mvn.py
+++ b/tools/maven/mvn.py
@@ -15,12 +15,14 @@
from __future__ import print_function
from optparse import OptionParser
+from os import path
from sys import stderr
from util import check_output
opts = OptionParser()
opts.add_option('--repository', help='maven repository id')
opts.add_option('--url', help='maven repository url')
+opts.add_option('-o')
opts.add_option('-a', help='action (valid actions are: install,deploy)')
opts.add_option('-v', help='gerrit version')
opts.add_option('-s', action='append', help='triplet of artifactId:type:path')
@@ -35,11 +37,13 @@
'-Dversion=%s' % args.v,
]
+self = path.dirname(path.abspath(__file__))
+mvn = ['mvn', '--file', path.join(self, 'fake_pom.xml')]
+
if 'install' == args.a:
- cmd = ['mvn', 'install:install-file'] + common
+ cmd = mvn + ['install:install-file'] + common
elif 'deploy' == args.a:
- cmd = [
- 'mvn',
+ cmd = mvn + [
'deploy:deploy-file',
'-DrepositoryId=%s' % args.repository,
'-Durl=%s' % args.url,
@@ -59,3 +63,10 @@
except Exception as e:
print('%s command failed: %s' % (args.a, e), file=stderr)
exit(1)
+
+with open(args.o, 'w') as fd:
+ if args.repository:
+ print('Repository: %s' % args.repository, file=fd)
+ if args.url:
+ print('URL: %s' % args.url, file=fd)
+ print('Version: %s' % args.v, file=fd)
diff --git a/tools/maven/package.defs b/tools/maven/package.defs
index b19701f..7306031 100644
--- a/tools/maven/package.defs
+++ b/tools/maven/package.defs
@@ -18,7 +18,7 @@
url = None,
jar = {},
src = {}):
- cmd = ['$(exe //tools/maven:mvn)', '-v', version]
+ cmd = ['$(exe //tools/maven:mvn)', '-v', version, '-o', '$OUT']
dep = []
for type,d in [('jar', jar), ('java-source', src)]:
@@ -30,7 +30,7 @@
name = 'install',
cmd = ' '.join(cmd + ['-a', 'install']),
deps = dep + ['//tools/maven:mvn'],
- out = '__fake.install__',
+ out = 'install.info',
)
if repository and url:
@@ -41,5 +41,5 @@
'--repository', repository,
'--url', url]),
deps = dep + ['//tools/maven:mvn'],
- out = '__fake.deploy__',
+ out = 'deploy.info',
)