Merge "Document Vim setup for Git commit messages"
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 31df2c3..e814daf 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -1205,13 +1205,6 @@
you need the <<capability_viewCaches,view caches capability>>.
-[[capability_generateHttpPassword]]
-=== Generate HTTP Password
-
-Allow the user to generate HTTP passwords for other users. Typically this would
-be assigned to a non-interactive users group.
-
-
[[capability_kill]]
=== Kill Task
diff --git a/Documentation/cmd-set-account.txt b/Documentation/cmd-set-account.txt
index 40f2378..f31f61b 100644
--- a/Documentation/cmd-set-account.txt
+++ b/Documentation/cmd-set-account.txt
@@ -26,12 +26,12 @@
Caller must be a member of the privileged 'Administrators' group,
or have been granted
link:access-control.html#capability_modifyAccount[the 'Modify Account' global capability].
+For security reasons only the members of the privileged 'Administrators'
+group can add or delete SSH keys for a user.
To set the HTTP password for the user account (option --http-password) or
to clear the HTTP password (option --clear-http-password) caller must be
-a member of the privileged 'Administrators' group, or have been granted
-link:access-control.html#capability_generateHttpPassword[the 'Generate HTTP Password' global capability]
-in addition to 'Modify Account' global capability.
+a member of the privileged 'Administrators' group.
== SCRIPTING
This command is intended to be used in scripts.
diff --git a/Documentation/dev-buck.txt b/Documentation/dev-buck.txt
index 0bf44d0..3b11382 100644
--- a/Documentation/dev-buck.txt
+++ b/Documentation/dev-buck.txt
@@ -389,6 +389,21 @@
/home/<user>/projects/jgit/org.eclipse.jgit/target/org.eclipse.jgit-3.3.0-SNAPSHOT.jar
----
+After `buck clean` and `buck build lib/jgit:jgit` the symbolic link that was
+created the first time is lost due to Buck's caching mechanism. This means that
+when a new version of the local artifact is deployed (by running `mvn package`
+in the JGit project in the example above), Buck is not aware of it, because it
+still has a stale version of it in its cache.
+
+To solve this problem and re-create the symbolic link, you don't need to wipe out
+the entire Buck cache. Just rebuilding the target with the `--no-cache` option
+does the job:
+
+----
+ buck clean
+ buck build --no-cache lib/jgit:jgit
+----
+
== Building against artifacts from custom Maven repositories
To build against custom Maven repositories, two modes of operations are
@@ -448,6 +463,25 @@
EOF
----
+[[clean-cache]]
+=== Cleaning The Buck Cache
+
+The cache for the Gerrit Code Review project is located in
+`~/.gerritcodereview/buck-cache/cache`.
+
+The Buck cache should never need to be manually deleted. If you find yourself
+deleting the Buck cache regularly, then it is likely that there is something
+wrong with your environment or your workflow.
+
+If you really do need to clean the cache manually, then:
+
+----
+ rm -rf ~/.gerritcodereview/buck-cache/cache
+----
+
+Note that the root `buck-cache` folder should not be deleted as this is where
+downloaded artifacts are stored.
+
[[buck-daemon]]
=== Using Buck daemon
@@ -473,7 +507,7 @@
Prepend the variable to Buck invocation instead:
----
- $ NO_BUCKD=1 buck build gerrit
+ NO_BUCKD=1 buck build gerrit
----
[[watchman]]
@@ -537,13 +571,13 @@
needs to be repeated, the unit test cache for that test must be removed first:
----
- $ rm -rf buck-out/bin/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/.AddRemoveGroupMembersIT/
+ rm -rf buck-out/bin/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/.AddRemoveGroupMembersIT/
----
After clearing the cache, the test can be run again:
----
- $ buck test //gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group:AddRemoveGroupMembersIT
+ buck test //gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group:AddRemoveGroupMembersIT
TESTING //gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group:AddRemoveGroupMembersIT
PASS 14,9s 8 Passed 0 Failed com.google.gerrit.acceptance.rest.group.AddRemoveGroupMembersIT
TESTS PASSED
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt
index 9b64ddd..6dc8d1c 100644
--- a/Documentation/dev-contributing.txt
+++ b/Documentation/dev-contributing.txt
@@ -193,7 +193,7 @@
might appear near the instance methods which they help (but may
also appear at the top).
* Getters and setters for the same instance field should usually
- be near each other baring a good reason not to.
+ be near each other barring a good reason not to.
* If you are using assisted injection, the factory for your class
should be before the instance members.
* Annotations should go before language keywords (final, private...) +
@@ -201,6 +201,9 @@
* Imports should be mostly alphabetical (uppercase sorts before
all lowercase, which means classes come before packages at the
same level).
+ * Prefer to open multiple AutoCloseable resources in the same
+ try-with-resources block instead of nesting the try-with-resources
+ blocks and increasing the indentation level more than necessary.
Wow that's a lot! But don't worry, you'll get the habit and most
of the code is organized this way already; so if you pay attention
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index a14aebc..3e76edc 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -1753,6 +1753,7 @@
----
import com.google.gerrit.extensions.annotations.Listen;
import com.google.gerrit.extensions.webui.PatchSetWebLink;;
+import com.google.gerrit.extensions.webui.WebLinkTarget;
@Listen
public class MyWeblinkPlugin implements PatchSetWebLink {
@@ -1762,23 +1763,11 @@
private String imageUrl = "http://placehold.it/16x16.gif";
@Override
- public String getLinkName() {
- return name;
- }
-
- @Override
- public String getPatchSetUrl(String project, String commit) {
- return String.format(placeHolderUrlProjectCommit, project, commit);
- }
-
- @Override
- public String getImageUrl() {
- return imageUrl;
- }
-
- @Override
- public String getTarget() {
- return "_blank";
+ public WebLinkInfo getPathSetWebLink(String projectName, String commit) {
+ return new WebLinkInfo(name,
+ imageUrl,
+ String.format(placeHolderUrlProjectCommit, project, commit),
+ WebLinkTarget.BLANK);
}
}
----
diff --git a/Documentation/install-j2ee.txt b/Documentation/install-j2ee.txt
index 4f438e5..f7252e0 100644
--- a/Documentation/install-j2ee.txt
+++ b/Documentation/install-j2ee.txt
@@ -109,15 +109,18 @@
`system_config`) is as simple as touching the context config file:
`'$JETTY_HOME'/contexts/gerrit.xml`
+[[tomcat]]
== Tomcat 7.x
-[TIP]
-If reverse proxy is used in front of Tomcat then see the configuration
-instructions for encoding slashes link:config-reverseproxy.html. Otherwise
-Tomcat must be configured to encode slashes, by adding
--Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true to
-CATALINA_OPTS environment variable. Excerpt from the documentation
-https://tomcat.apache.org/tomcat-7.0-doc/config/systemprops.html:
+If a reverse proxy is used in front of Tomcat then see the
+link:config-reverseproxy.html[configuration instructions for encoding
+slashes]. Otherwise Tomcat must be configured to encode slashes, by adding
+`-Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true` to the
+`CATALINA_OPTS` environment variable.
+
+Excerpt from the
+link:https://tomcat.apache.org/tomcat-7.0-doc/config/systemprops.html[
+documentation]:
----
Property org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH:
diff --git a/Documentation/prolog-cookbook.txt b/Documentation/prolog-cookbook.txt
index 5bff6bf..e069d85 100644
--- a/Documentation/prolog-cookbook.txt
+++ b/Documentation/prolog-cookbook.txt
@@ -264,7 +264,7 @@
[[HowToWriteSubmitType]]
== How to write submit type
-Writing custom submit type logic in Prolog is the similar top
+Writing custom submit type logic in Prolog is similar to
xref:HowToWriteSubmitRules[writing submit rules]. The only difference is that
one has to implement a `submit_type` predicate (instead of the `submit_rule`)
and that the return result of the `submit_type` has to be an atom that
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index b1f6bcc..24da5e4 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -908,7 +908,7 @@
.Request
----
- GET /a/accounts/self/preferences HTTP/1.0
+ PUT /a/accounts/self/preferences HTTP/1.0
Content-Type: application/json;charset=UTF-8
{
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 3a2aebd..20648dc 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -930,6 +930,112 @@
]
----
+[[branch-options]]
+==== Branch Options
+
+Limit(n)::
+Limit the number of branches to be included in the results.
++
+.Request
+----
+ GET /projects/testproject/branches?n=1 HTTP/1.0
+----
++
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json;charset=UTF-8
+
+ )]}'
+ [
+ {
+ "ref": "HEAD",
+ "revision": "master",
+ "can_delete": false
+ }
+ ]
+----
+
+Skip(s)::
+Skip the given number of branches from the beginning of the list.
++
+.Request
+----
+ GET /projects/testproject/branches?n=1&s=0 HTTP/1.0
+----
++
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json;charset=UTF-8
+
+ )]}'
+ [
+ {
+ "ref": "HEAD",
+ "revision": "master",
+ "can_delete": false
+ }
+ ]
+----
+
+Substring(m)::
+Limit the results to those projects that match the specified substring.
++
+List all projects that match substring `test`:
++
+.Request
+----
+ GET /projects/testproject/branches?m=test HTTP/1.0
+----
++
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json;charset=UTF-8
+
+ )]}'
+ [
+ {
+ "ref": "refs/heads/test1",
+ "revision": "9c9d08a438e55e52f33b608415e6dddd9b18550d",
+ "can_delete": true
+ }
+ ]
+----
+
+Regex(r)::
+Limit the results to those branches that match the specified regex.
+Boundary matchers '^' and '$' are implicit. For example: the regex 't*' will
+match any branches that start with 'test' and regex '*t' will match any
+branches that end with 'test'.
++
+List all branches that match regex `t.*1`:
++
+.Request
+----
+ GET /projects/testproject/branches?r=t.*1 HTTP/1.0
+----
++
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json;charset=UTF-8
+
+ )]}'
+ [
+ {
+ "ref": "refs/heads/test1",
+ "revision": "9c9d08a438e55e52f33b608415e6dddd9b18550d",
+ "can_delete": true
+ }
+ ]
+----
+
[[get-branch]]
=== Get Branch
--
diff --git a/Documentation/user-upload.txt b/Documentation/user-upload.txt
index 9bc10f4..a9deba8 100644
--- a/Documentation/user-upload.txt
+++ b/Documentation/user-upload.txt
@@ -155,6 +155,7 @@
git push ssh://john.doe@git.example.com:29418/kernel/common HEAD:refs/for/experimental%topic=driver/i42
====
+[[review_labels]]
Review labels can be applied to the change by using the `label` (or `l`)
option in the reference:
diff --git a/ReleaseNotes/ReleaseNotes-2.10.txt b/ReleaseNotes/ReleaseNotes-2.10.txt
new file mode 100644
index 0000000..78134694
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.10.txt
@@ -0,0 +1,636 @@
+Release notes for Gerrit 2.10
+=============================
+
+
+Gerrit 2.10 is now available:
+
+link:https://gerrit-releases.storage.googleapis.com/gerrit-2.10.war[
+https://gerrit-releases.storage.googleapis.com/gerrit-2.10.war]
+
+Gerrit 2.10 includes the bug fixes done with
+link:ReleaseNotes-2.9.1.html[Gerrit 2.9.1].
+These bug fixes are *not* listed in these release notes.
+
+Important Notes
+---------------
+
+
+*WARNING:* This release contains schema changes. To upgrade:
+----
+ java -jar gerrit.war init -d site_path
+ java -jar gerrit.war reindex --recheck-mergeable -d site_path
+----
+
+*WARNING:* Upgrading to 2.10.x requires the server be first upgraded to 2.1.7 (or
+a later 2.1.x version), and then to 2.10.x. If you are upgrading from 2.2.x.x or
+later, you may ignore this warning and upgrade directly to 2.10.x.
+
+*WARNING:* The `auth.allowGoogleAccountUpgrade` setting is no longer supported.
+
+
+Release Highlights
+------------------
+
+
+* Support for externally loaded plugins.
++
+Plugins can be implemented in Scala or Groovy using the
+link:https://gerrit-review.googlesource.com/\#/admin/projects/plugins/scripting/groovy-provider[
+Groovy provider] and
+link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/scripting/scala-provider[
+Scala provider] plugins.
+
+* Customizable 'My' menu.
++
+Users can customize the contents of the 'My' menu in the top menu. Administrators
+can configure the default contents of the menu.
+
+
+New Features
+------------
+
+
+Web UI
+~~~~~~
+
+
+Global
+^^^^^^
+
+* Add 'All-Users' project to store meta data for all users.
+
+* Administrators can customize the default contents of the 'My' menu.
+
+* Add 'My' > 'Groups' menu entry that shows the list of own groups.
+
+* Allow UiActions to perform redirects without JavaScript.
+
+
+Change Screen
+^^^^^^^^^^^^^
+
+
+* Remove 'send email' checkbox from reply box on change screen.
+
+* Do not linkify trailing dot or comma in messages.
++
+As linkifying trailing dots and trailing commas does more harm than
+good, we only treat dots and commas as being part of urls, if they are
+neither followed by whitespace nor occur at the end of a string.
+
+* Improve message when removing a reviewer.
+
+* Display avatar for author, committer, and change owner.
+
+* Remove message box when editing topic of change.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2573[Issue 2573]:
+Add option to quickly add current user as reviewer of a change.
+
+* Link project name to dashboard.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2667[Issue 2667]:
+Allow to customize Submit button label and tooltip.
+
+
+Side-by-Side Diff Screen
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Allow the user to select the syntax highlighter.
+
+* Add `Shift-a` keybinding to show/hide left side.
+
+* Allow to toggle empty pane for added and deleted files.
+
+* Add syntax highlighting of the commit message.
+
+
+Change List / Dashboards
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Remove age operator when drilling down from a dashboard to a query.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2646[Issue 2646]:
+Add option to show Change-ID in the change table.
+
+* Make the own user dashboard available under '/dashboard/self'.
+
+* Add 'R' key binding to refresh custom dashboards.
++
+Account dashboards, search results and the change screen refresh their content
+when 'R' is pressed. The same binding is added for custom dashboards.
+
+
+Project Screens
+^^^^^^^^^^^^^^^
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2751[Issue 2751]:
+Add support for filtering by regex in project list screen.
+
+* Disable content merge option if project's merge strategy is fast forward only.
+
+* Add branch actions to 'Projects > Branches' view.
+
+User Preferences
+^^^^^^^^^^^^^^^^
+
+
+* Users can customize the contents of the 'My' menu from the preferences
+screen.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2628[Issue 2628]:
+Replace 'Display name in review category' preference with a list of options.
++
+Including new options 'Show Abbreviated Name' to display abbreviated reviewer
+names and 'Show Username' to show usernames in the change list.
+
+
+Secondary Index / Search
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+* Allow to search projects by prefix.
+
+* Add search fields for number of changed lines.
+
+* Add suggestions for 'is:pending' and 'status:pending'.
+
+* Add 'pending' as alias for 'open'.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2545[Issue 2545]:
+Support `topic:""` to find changes with no topic.
+
+* Search more fields in the default search query.
++
+If a search is given with only a text, search over a variety of fields
+rather than just the project name.
+
+
+ssh
+~~~
+
+
+* Expose SSHD backend in
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/cmd-show-connections.html[
+`show connections`] SSH command.
+
+* Add support for JCE (Java Cryptography Extension) ciphers.
+
+REST API
+~~~~~~~~
+
+
+General
+^^^^^^^
+
+
+* Remove `kind` attribute from REST containers.
+
+* Support `AcceptsPost` on non top-level REST collections.
+
+* Accept `HEAD` in RestApiServlet.
+
+Accounts
+^^^^^^^^
+
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-accounts.html#get-user-preferences[
+Get user preferences].
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-accounts.html#set-user-preferences[
+Set user preferences].
+
+Changes
+^^^^^^^
+
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2338[Issue 2338]:
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-changes.html#create-change[
+Create change].
+
+* Add `other-branches` option on
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-changes.html#get-mergeable[
+Get mergeable] endpoint.
++
+If the `other-branches` option is specified, the mergeability will also be
+checked for all other branches.
+
+Config
+^^^^^^
+
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-config.html#list-tasks[
+List tasks].
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-config.html#get-task[
+Get task].
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-config.html#delete-task[
+Delete task].
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-config.html#list-caches[
+List caches].
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-config.html#flush-cache[
+Flush cache].
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-config.html#flush-several-caches[
+Flush several caches].
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-config.html#flush-all-caches[
+Flush all caches].
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-config.html#get-summary[
+Get server summary].
+
+Projects
+^^^^^^^^
+
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-projects.html#ban-commit[
+Ban commits].
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-projects.html#get-content[
+Get the content of a file from a certain commit].
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2604[Issue 2604]:
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-projects.html#get-commit[
+Get an arbitrary commit from a project].
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-projects.html#get-reflog[
+Get the reflog of a branch].
+
+* Add option 'S' to
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-projects.html#list-projects[
+list projects endpoint] to support query offset.
+
+
+Daemon
+~~~~~~
+
+
+* Add change subject to output of change URL on push.
+
+* Indicate trivial rebase and commit message update on push.
+
+* Add support for
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/user-upload.html#review_labels[
+adding review labels on changes] during git push.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2634[Issue 2634]:
+Add change kind to PatchSetCreatedEvent.
+
+
+Configuration
+~~~~~~~~~~~~~
+
+* Use
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/config-gerrit.html#core.useRecursiveMerge[
+recursive merge] by default.
+
+* Allow to configure the
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/config-gerrit.html#download.archive[
+available download archive formats].
+
+* Add support for
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/database-setup.html#createdb_maxdb[
+SAP MaxDB].
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2041[Issue 2041]:
+Allow
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/config-labels.html#label_defaultValue[
+configuration of a default value for a label].
+
+* Allow projects to
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/config-project-config.html#mimetype-section[
+configure MIME types for files].
+
+* Allow to configure
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/config-gerrit.html#gc[
+periodic garbage collection of all projects].
+
+* Remove `auth.allowGoogleAccountUpgrade` setting.
++
+It's been more than 5 years since Gerrit ran on Google AppEngine. It is assumed
+that everyone has upgraded their installations to a modern 2.x based server, and
+will not need to have this upgrade path enabled.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2618[Issue 2618]:
+Remove `label.Label-Name.abbreviation` setting.
++
+The setting was no longer used, so it has been removed.
+
+* New `httpd.registerMBeans` setting.
++
+The
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/config-gerrit.html#httpd.registerMBeans[
+`httpd.registerMBeans` setting] allows to enable (or disable) registration of
+Jetty MBeans for Java JMX.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2600[Issue 2600]:
+Add documentation of how to
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/install-j2ee.html#tomcat[
+configure Tomcat] to allow embedded slashes.
+
+
+Misc
+~~~~
+
+* Don't allow empty user name and passwords in InternalAuthBackend.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2596[Issue 2596]:
+Add change-owner parameter to gerrit hooks.
+
+
+Plugins
+~~~~~~~
+
+* Support for externally loaded plugins.
++
+Plugins can be implemented in Scala or Groovy using the
+link:https://gerrit-review.googlesource.com/\#/admin/projects/plugins/scripting/groovy-provider[
+Groovy provider] and
+link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/scripting/scala-provider[
+Scala provider] plugins.
+
+* Allow plugins to replace the WebSession implementation.
++
+Plugins can replace the existing implementation with the statement:
+`DynamicItem.bind(binder(), WebSession.class).to(...);`
+in a module designated as a `<Gerrit-HttpModule>` in the manifest.
++
+Just the Cache implementation used for web sessions can be changed
+by binding to a subclass of the now abstract `CacheBasedWebSession`
+which supplies the Cache in the superclass constructor.
++
+This is a step towards solving web session issues with multi-master.
++
+The link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/websession-flatfile[
+websession-flatfile plugin] replaces the built-in Gerrit WebSession implementation
+with one that uses a flat file based cache.
+
+* Allow http and ssh plugins to replace the Gerrit-provided DynamicItem.
+
+* New extension point to listen to usage data published events.
++
+Plugins implementing the `UsageDataPublishedListener` can listen to
+events published about usage data.
+
+* New extension point to link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/dev-plugins.html#pre-upload-hook[
+register JGit PreUploadHook].
++
+Plugins may register PreUploadHook instances in order to get
+notified when JGit is about to upload a pack. This may be useful
+for those plugins which would like to monitor usage in Git
+repositories.
+
+* New link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/config-validation.html#pre-upload-validation[
+pre-upload validation extension point].
++
+Plugins implementing the `UploadValidationListener` interface can
+perform additional validation checks before any upload operations
+(clone, fetch, pull). The validation is executed right before Gerrit
+begins to send a pack back to the git client.
+
+* New link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/dev-plugins.html#links-to-external-tools[
+external tool links extension points].
++
+Plugins can now contribute project links that will be displayed on the project
+list screen in the 'Repository Browser' column, and revision links that will be
+shown on the change screen.
+
+* Allow creation of persistent caches after server is started.
++
+This enables plugins to create own persistent caches when they are
+installed.
+
+* Make gerrit's HttpServletRequest and HttpServletResponse visible to http
+plugins.
+
+* New extensions in the Java Plugin API:
+
+** Query changes
+** Create/get/list projects
+** Get/set review status
+** Create change
+** Get account
+** Star/unstar changes
+** Check if revision needs rebase
+
+Bug Fixes
+---------
+
+General
+~~~~~~~
+
+* Use fixed rate instead of fixed delay for log file compression.
++
+Log file compression was scheduled using a fixed delay. This caused the start
+times to drift over time. Use a fixed rate instead so that the compression
+reoccurs at the same time every day.
+
+Web UI
+~~~~~~
+
+General
+^^^^^^^
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2595[Issue 2595]:
+Make gitweb redirect to login.
++
+Gitweb redirects to the login page if the user isn't currently logged.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2631[Issue 2631]:
+Re-arrange info at footer of Gerrit web UI pages.
++
+Move the Gerrit info link so that there are no links close to the next page link.
+
+Changes
+^^^^^^^
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=527[Issue 527]:
+Preserve line breaks in inline and review comments.
+
+* Always show 'No Score' as label help for zero votings.
+
+* Only reset the edited commit message text on cancel.
+
+* Only include message on quick approve if reply is open.
+
+* List reviewers with dummy approvals on closed changes.
+
+
+Side-By-Side Diff
+^^^^^^^^^^^^^^^^^
+
+* Fix line length column margin to appear at correct column.
+
+* Give B side full width when A side is hidden.
+
+* Fix scroll alignment when showing hidden A side.
+
+* Bind Shift-N to search-prev in vim mode.
+
+* Allow text selection in diff header.
+
+* Display diff header on mode changes and renames.
+
+* Document Shift-{Left,Right} in `?` help popup.
+
+* Show `[` and `]` shortcut keys in nav arrow tooltips.
+
+* Disable "Render = Slow" mode on files over 4000 lines.
+
+* Keep keyboard bindings alive after click in padding.
+
+* Jump to the first change on either side.
+
+* Expand margin between paragraphs in comments.
+
+* Include content on identical files with mode change.
+
+
+User Settings
+^^^^^^^^^^^^^
+
+* Avoid loading all SSH keys when adding a new one.
+
+
+Secondary Index / Search
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+* Omit corrupt changes from search results.
+
+* Allow illegal label names from default search predicate.
+
+REST
+~~~~
+
+General
+^^^^^^^
+
+* Fix REST API responses for 3xx and 4xx classes.
+
+
+Changes
+^^^^^^^
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2583[Issue 2583]:
+Reject inline comments on files that do not exist in the patch set.
+
+* Allow forcing mergeability check on
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-changes.html#get-mergeable[
+Get mergeable].
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2622[Issue 2622]:
+Respect patch set visibility for messages.
++
+Messages retrieval didn't check for patch set visbility and thus messages for
+draft patch sets were returned back to the client.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2782[Issue 2782]:
+Add missing documentation of the
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-changes.html#get-related-changes[
+Get Related Changes] endpoint.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2723[Issue 2723]:
+Clarify the response info in the
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-changes.html#get-change-detail[
+Get Change Detail] endpoint.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2693[Issue 2693]:
+Clarify the response info in the
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/rest-api-changes.html#list-comments[
+List Comments] endpoint.
+
+SSH
+~~~
+
+
+* Prevent double authentication for the same public key.
++
+This is a workaround for link:https://issues.apache.org/jira/browse/SSHD-300[
+SSHD-300].
+
+* Let `kill` SSH command only kill tasks that are visible to the caller.
+
+* Require 'Administrate Server' capability to see server summary output from
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.10/cmd-show-caches.html[
+`show-caches`] command.
+
+Daemon
+~~~~~~
+
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2284[Issue 2284]:
+More detailed error message when failing to upload new change.
++
+When the uploaded change cannot be created on the underlying Git repository, a
+more descriptive error message is displayed on both client and server side. This
+allows to troubleshoot internal errors (e.g. JGit lock failures or other causes)
+and help out in the resolution.
+
+* Enforce HTTP password checking on gitBasicAuth.
+
+* Fix missing commit messages on submodule direct pushes.
++
+The commit message in superproject was missing on submodule's
+directly pushed changes.
+
+
+Plugins
+~~~~~~~
+
+General
+^^^^^^^
+
+
+* Invoke `StartPluginListener` and `ReloadPluginListener` only after start/reload
+is fully done.
+
+* Set `Last-Modified` on cached Documentation resources.
+
+* Return HTTP 304 for not modified SmallResources.
+
+* Fix ChangeListener auto-registered implementations.
+
+Replication
+^^^^^^^^^^^
+
+
+* Move replication logs into a separate file.
+
+* Promote replication scheduled logs to info.
+
+* Show replication ID in the log and in show-queue command.
+
+
+Upgrades
+--------
+
+
+* Update Guava to 17.0
+
+* Update Guice to 4.0-beta5
+
+* Update GWT to 2.6.1
+
+* Update httpclient to 4.3.4
+
+* Update httpcore to 4.3.2
+
+* Update Jcraft SSH to 0.1.51
+
+* Update Jetty to 9.2
+
+* Update JGit to 3.4.0.201406110918-r
+
+* Update log4j to 1.2.17
+
+* Update Servlet API to 8.0.5
+
+* Update slf4j to 1.7.7
+
+* Update Velocity to 1.7
+
diff --git a/ReleaseNotes/ReleaseNotes-2.11.txt b/ReleaseNotes/ReleaseNotes-2.11.txt
index 23f3247..c12d913 100644
--- a/ReleaseNotes/ReleaseNotes-2.11.txt
+++ b/ReleaseNotes/ReleaseNotes-2.11.txt
@@ -61,7 +61,10 @@
Other
~~~~~
-TODO
+The 'Generate HTTP Password' capability has been removed to close a
+security vulnerability. Now only administrators are allowed to generate
+and delete other user's http passwords via the REST or SSH interface.
+We would encourage you to clean up your project.config setting after upgrading.
Upgrades
--------
diff --git a/ReleaseNotes/index.txt b/ReleaseNotes/index.txt
index 8e7cac6..47f453b 100644
--- a/ReleaseNotes/index.txt
+++ b/ReleaseNotes/index.txt
@@ -1,6 +1,11 @@
Gerrit Code Review - Release Notes
==================================
+[[2_10]]
+Version 2.10.x
+--------------
+* link:ReleaseNotes-2.10.html[2.10]
+
[[2_9]]
Version 2.9.x
-------------
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
index 2e28655..d14a5a8 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
@@ -114,6 +114,86 @@
devCommit, false)), toBranchInfoList(r));
}
+ @Test
+ public void listBranchesUsingPagination() throws Exception {
+ pushTo("refs/heads/master");
+ pushTo("refs/heads/someBranch1");
+ pushTo("refs/heads/someBranch2");
+ pushTo("refs/heads/someBranch3");
+
+ // using only limit
+ RestResponse r =
+ adminSession.get("/projects/" + project.get() + "/branches?n=4");
+ List<BranchInfo> result = toBranchInfoList(r);
+ assertEquals(4, result.size());
+ assertEquals("HEAD", result.get(0).ref);
+ assertEquals("refs/meta/config", result.get(1).ref);
+ assertEquals("refs/heads/master", result.get(2).ref);
+ assertEquals("refs/heads/someBranch1", result.get(3).ref);
+
+ // limit higher than total number of branches
+ r = adminSession.get("/projects/" + project.get() + "/branches?n=25");
+ result = toBranchInfoList(r);
+ assertEquals(6, result.size());
+ assertEquals("HEAD", result.get(0).ref);
+ assertEquals("refs/meta/config", result.get(1).ref);
+ assertEquals("refs/heads/master", result.get(2).ref);
+ assertEquals("refs/heads/someBranch1", result.get(3).ref);
+ assertEquals("refs/heads/someBranch2", result.get(4).ref);
+ assertEquals("refs/heads/someBranch3", result.get(5).ref);
+
+ // using skip only
+ r = adminSession.get("/projects/" + project.get() + "/branches?s=2");
+ result = toBranchInfoList(r);
+ assertEquals(4, result.size());
+ assertEquals("refs/heads/master", result.get(0).ref);
+ assertEquals("refs/heads/someBranch1", result.get(1).ref);
+ assertEquals("refs/heads/someBranch2", result.get(2).ref);
+ assertEquals("refs/heads/someBranch3", result.get(3).ref);
+
+ // skip more branches than the number of available branches
+ r = adminSession.get("/projects/" + project.get() + "/branches?s=7");
+ result = toBranchInfoList(r);
+ assertEquals(0, result.size());
+
+ // using skip and limit
+ r = adminSession.get("/projects/" + project.get() + "/branches?s=2&n=2");
+ result = toBranchInfoList(r);
+ assertEquals(2, result.size());
+ assertEquals("refs/heads/master", result.get(0).ref);
+ assertEquals("refs/heads/someBranch1", result.get(1).ref);
+ }
+
+ @Test
+ public void listBranchesUsingFilter() throws Exception {
+ pushTo("refs/heads/master");
+ pushTo("refs/heads/someBranch1");
+ pushTo("refs/heads/someBranch2");
+ pushTo("refs/heads/someBranch3");
+
+ //using substring
+ RestResponse r =
+ adminSession.get("/projects/" + project.get() + "/branches?m=some");
+ List<BranchInfo> result = toBranchInfoList(r);
+ assertEquals(3, result.size());
+ assertEquals("refs/heads/someBranch1", result.get(0).ref);
+ assertEquals("refs/heads/someBranch2", result.get(1).ref);
+ assertEquals("refs/heads/someBranch3", result.get(2).ref);
+
+ r = adminSession.get("/projects/" + project.get() + "/branches?m=Branch");
+ result = toBranchInfoList(r);
+ assertEquals(3, result.size());
+ assertEquals("refs/heads/someBranch1", result.get(0).ref);
+ assertEquals("refs/heads/someBranch2", result.get(1).ref);
+ assertEquals("refs/heads/someBranch3", result.get(2).ref);
+
+ //using regex
+ r = adminSession.get("/projects/" + project.get() + "/branches?r=.*ast.*r");
+ result = toBranchInfoList(r);
+ assertEquals(1, result.size());
+ assertEquals("refs/heads/master", result.get(0).ref);
+ }
+
private RestResponse GET(String endpoint) throws IOException {
return adminSession.get(endpoint);
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
index c6c2d50..e4b0381 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
@@ -61,9 +61,6 @@
/** Can flush any cache except the active web_sessions cache. */
public static final String FLUSH_CACHES = "flushCaches";
- /** Can generate HTTP passwords for user other than self. */
- public static final String GENERATE_HTTP_PASSWORD = "generateHttpPassword";
-
/** Can terminate any task using the kill command. */
public static final String KILL_TASK = "killTask";
@@ -112,7 +109,6 @@
NAMES_ALL.add(CREATE_PROJECT);
NAMES_ALL.add(EMAIL_REVIEWERS);
NAMES_ALL.add(FLUSH_CACHES);
- NAMES_ALL.add(GENERATE_HTTP_PASSWORD);
NAMES_ALL.add(KILL_TASK);
NAMES_ALL.add(MODIFY_ACCOUNT);
NAMES_ALL.add(PRIORITY);
diff --git a/gerrit-extension-api/BUCK b/gerrit-extension-api/BUCK
index aad79d7..0c1b6a8 100644
--- a/gerrit-extension-api/BUCK
+++ b/gerrit-extension-api/BUCK
@@ -47,7 +47,7 @@
java_doc(
name = 'extension-api-javadoc',
title = 'Gerrit Review Extension API Documentation',
- pkg = 'com.google.gerrit.extensions',
+ pkgs = ['com.google.gerrit.extensions'],
paths = ['src/main/java'],
srcs = SRCS,
deps = [
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/BranchWebLink.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/BranchWebLink.java
index bc7a1e5..65a45dc 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/BranchWebLink.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/BranchWebLink.java
@@ -15,16 +15,17 @@
package com.google.gerrit.extensions.webui;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
+import com.google.gerrit.extensions.common.WebLinkInfo;
@ExtensionPoint
-public interface BranchWebLink extends WebLink {
+public interface BranchWebLink {
/**
* URL to branch in external service.
*
* @param projectName Name of the project
* @param branchName Name of the branch
- * @return url to branch in external service.
+ * @return WebLinkInfo that links to branch in external service.
*/
- String getBranchUrl(String projectName, String branchName);
+ WebLinkInfo getBranchWebLink(String projectName, String branchName);
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/FileWebLink.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/FileWebLink.java
index ee3c62f..1bfbd99 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/FileWebLink.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/FileWebLink.java
@@ -15,9 +15,10 @@
package com.google.gerrit.extensions.webui;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
+import com.google.gerrit.extensions.common.WebLinkInfo;
@ExtensionPoint
-public interface FileWebLink extends WebLink {
+public interface FileWebLink {
/**
* URL to file in external service.
@@ -25,7 +26,7 @@
* @param projectName Name of the project
* @param revision Name of the revision (e.g. branch or commit ID)
* @param fileName Name of the file
- * @return url to project in external service.
+ * @return WebLinkInfo that links to project in external service.
*/
- String getFileUrl(String projectName, String revision, String fileName);
+ WebLinkInfo getFileWebLink(String projectName, String revision, String fileName);
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/PatchSetWebLink.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/PatchSetWebLink.java
index b6086f2..8aaedaf 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/PatchSetWebLink.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/PatchSetWebLink.java
@@ -14,16 +14,17 @@
package com.google.gerrit.extensions.webui;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
+import com.google.gerrit.extensions.common.WebLinkInfo;
@ExtensionPoint
-public interface PatchSetWebLink extends WebLink {
+public interface PatchSetWebLink {
/**
* URL to patch set in external service.
*
* @param projectName Name of the project
* @param commit Commit of the patch set
- * @return url to patch set in external service.
+ * @return WebLinkInfo that links to patch set in external service.
*/
- String getPatchSetUrl(final String projectName, final String commit);
+ WebLinkInfo getPathSetWebLink(final String projectName, final String commit);
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/ProjectWebLink.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/ProjectWebLink.java
index 61e9982..204c51d 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/ProjectWebLink.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/ProjectWebLink.java
@@ -15,15 +15,16 @@
package com.google.gerrit.extensions.webui;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
+import com.google.gerrit.extensions.common.WebLinkInfo;
@ExtensionPoint
-public interface ProjectWebLink extends WebLink {
+public interface ProjectWebLink {
/**
* URL to project in external service.
*
* @param projectName Name of the project
- * @return url to project in external service.
+ * @return WebLinkInfo that links to project in external service.
*/
- String getProjectUrl(String projectName);
+ WebLinkInfo getProjectWeblink(String projectName);
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/WebLink.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/WebLink.java
deleted file mode 100644
index 4065c68..0000000
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/WebLink.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package com.google.gerrit.extensions.webui;
-
-public interface WebLink {
-
- public static class Target {
- public final static String BLANK = "_blank";
- public final static String SELF = "_self";
- public final static String PARENT = "_parent";
- public final static String TOP = "_top";
- }
- /**
- * The link-name displayed in UI.
- *
- * @return name of link or title of the link if image URL is available.
- */
- String getLinkName();
-
- /**
- * URL of image to be displayed
- *
- * @return URL to image for link or null for using a text-only link.
- * Recommended image size is 16x16.
- */
- String getImageUrl();
-
- /**
- * Target window in which the link should be opened (e.g. "_blank", "_self".).
- *
- * @return link target, if null the link is opened in the current window
- */
- String getTarget();
-}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/WebLinkTarget.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/WebLinkTarget.java
new file mode 100644
index 0000000..fc27c25
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/WebLinkTarget.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.gerrit.extensions.webui;
+
+/**
+ * Class that holds target defaults for WebLink anchors.
+ */
+public class WebLinkTarget {
+ /**
+ * Opens the link in a new window or tab
+ */
+ public final static String BLANK = "_blank";
+ /**
+ * Opens the link in the frame it was clicked.
+ */
+ public final static String SELF = "_self";
+ /**
+ * Opens link in parent frame.
+ */
+ public final static String PARENT = "_parent";
+ /**
+ * Opens link in the full body of the window.
+ */
+ public final static String TOP = "_top";
+}
diff --git a/gerrit-gwtexpui/BUCK b/gerrit-gwtexpui/BUCK
index e06bf17..b266e126 100644
--- a/gerrit-gwtexpui/BUCK
+++ b/gerrit-gwtexpui/BUCK
@@ -8,10 +8,10 @@
SRC + 'clippy/client/clippy.css',
SRC + 'clippy/client/clippy.swf',
],
+ provided_deps = ['//lib/gwt:user'],
deps = [
':SafeHtml',
':UserAgent',
- '//lib/gwt:user',
'//lib:LICENSE-clippy',
],
visibility = ['PUBLIC'],
@@ -21,7 +21,7 @@
name = 'CSS',
srcs = glob([SRC + 'css/rebind/*.java']),
resources = [SRC + 'css/CSS.gwt.xml'],
- deps = ['//lib/gwt:dev'],
+ provided_deps = ['//lib/gwt:dev'],
visibility = ['PUBLIC'],
)
@@ -33,10 +33,10 @@
SRC + 'globalkey/client/KeyConstants.properties',
SRC + 'globalkey/client/key.css',
],
+ provided_deps = ['//lib/gwt:user'],
deps = [
':SafeHtml',
':UserAgent',
- '//lib/gwt:user',
],
visibility = ['PUBLIC'],
)
@@ -53,7 +53,7 @@
srcs = glob([SRC + 'progress/client/*.java']),
gwt_xml = SRC + 'progress/Progress.gwt.xml',
resources = [SRC + 'progress/client/progress.css'],
- deps = ['//lib/gwt:user'],
+ provided_deps = ['//lib/gwt:user'],
visibility = ['PUBLIC'],
)
@@ -62,7 +62,7 @@
srcs = glob([SRC + 'safehtml/client/*.java']),
gwt_xml = SRC + 'safehtml/SafeHtml.gwt.xml',
resources = [SRC + 'safehtml/client/safehtml.css'],
- deps = ['//lib/gwt:user'],
+ provided_deps = ['//lib/gwt:user'],
visibility = ['PUBLIC'],
)
@@ -84,7 +84,7 @@
name = 'UserAgent',
srcs = glob([SRC + 'user/client/*.java']),
gwt_xml = SRC + 'user/User.gwt.xml',
- deps = ['//lib/gwt:user'],
+ provided_deps = ['//lib/gwt:user'],
visibility = ['PUBLIC'],
)
@@ -94,3 +94,17 @@
provided_deps = ['//lib:servlet-api-3_1'],
visibility = ['PUBLIC'],
)
+
+java_library(
+ name = 'client-src-lib',
+ srcs = [],
+ resources = glob(
+ [SRC + n for n in [
+ 'clippy/**/*',
+ 'globalkey/**/*',
+ 'safehtml/**/*',
+ 'user/**/*',
+ ]]
+ ),
+ visibility = ['PUBLIC'],
+)
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/WebLinkInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/WebLinkInfo.java
index 3ed91be..45731f7 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/WebLinkInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/WebLinkInfo.java
@@ -15,6 +15,8 @@
package com.google.gerrit.client;
import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.Image;
public class WebLinkInfo extends JavaScriptObject {
@@ -25,4 +27,22 @@
protected WebLinkInfo() {
}
+
+ public final Anchor toAnchor() {
+ Anchor a = new Anchor();
+ a.setHref(url());
+ if (target() != null && !target().isEmpty()) {
+ a.setTarget(target());
+ }
+ if (imageUrl() != null && !imageUrl().isEmpty()) {
+ Image img = new Image();
+ img.setAltText(name());
+ img.setUrl(imageUrl());
+ img.setTitle(name());
+ a.getElement().appendChild(img.getElement());
+ } else {
+ a.setText("(" + name() + ")");
+ }
+ return a;
+ }
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
index 9a6dae4..ae81410 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
@@ -396,22 +396,8 @@
c.toBranch(new Branch.NameKey(getProjectKey(), k.ref()))));
}
if (k.web_links() != null) {
- for (WebLinkInfo weblink : Natives.asList(k.web_links())) {
- Anchor a = new Anchor();
- a.setHref(weblink.url());
- if (weblink.target() != null && !weblink.target().isEmpty()) {
- a.setTarget(weblink.target());
- }
- if (weblink.imageUrl() != null && !weblink.imageUrl().isEmpty()) {
- Image img = new Image();
- img.setAltText(weblink.name());
- img.setUrl(weblink.imageUrl());
- img.setTitle(weblink.name());
- a.getElement().appendChild(img.getElement());
- } else {
- a.setText("(" + weblink.name() + ")");
- }
- actionsPanel.add(a);
+ for (WebLinkInfo webLink : Natives.asList(k.web_links())) {
+ actionsPanel.add(webLink.toAnchor());
}
}
if (k.actions() != null) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
index af01ad3..3d1b96061 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
@@ -238,21 +238,7 @@
}
for (WebLinkInfo weblink : webLinks) {
- Anchor a = new Anchor();
- a.setHref(weblink.url());
- if (weblink.target() != null && !weblink.target().isEmpty()) {
- a.setTarget(weblink.target());
- }
- if (weblink.imageUrl() != null && !weblink.imageUrl().isEmpty()) {
- Image img = new Image();
- img.setAltText(weblink.name());
- img.setUrl(weblink.imageUrl());
- img.setTitle(weblink.name());
- a.getElement().appendChild(img.getElement());
- } else {
- a.setText("(" + weblink.name() + ")");
- }
- p.add(a);
+ p.add(weblink.toAnchor());
}
}
}
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 20eb9b0..9bdf49a 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
@@ -433,7 +433,7 @@
reviewMode.setVisible(!editMode.isVisible());
editFileAction = new EditFileAction(
new PatchSet.Id(changeId, edit == null ? rev._number() : 0),
- "", "", style.replyBox(), editMessage, reply);
+ "", "", style, editMessage, reply);
} else {
editMode.setVisible(false);
addFile.setVisible(false);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
index b32a31b..1705675 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
@@ -126,35 +126,22 @@
RevisionInfo revInfo) {
GitwebLink gw = Gerrit.getGitwebLink();
if (gw != null && gw.canLink(revInfo)) {
- addWebLink(gw.toRevision(change.project(), revision),
- gw.getLinkName(), null, null);
+ toAnchor(gw.toRevision(change.project(), revision),
+ gw.getLinkName());
}
JsArray<WebLinkInfo> links = revInfo.web_links();
if (links != null) {
for (WebLinkInfo link : Natives.asList(links)) {
- addWebLink(link.url(), parenthesize(link.name()), link.imageUrl(),
- link.target());
+ webLinkPanel.add(link.toAnchor());
}
}
}
- private void addWebLink(String href, String name, String imageUrl,
- String target) {
+ private void toAnchor(String href, String name) {
Anchor a = new Anchor();
a.setHref(href);
- if (target != null && !target.isEmpty()) {
- a.setTarget(target);
- }
- if (imageUrl != null && !imageUrl.isEmpty()) {
- Image img = new Image();
- img.setAltText(name);
- img.setUrl(imageUrl);
- img.setTitle(name);
- a.getElement().appendChild(img.getElement());
- } else {
- a.setText(name);
- }
+ a.setText(name);
webLinkPanel.add(a);
}
@@ -198,14 +185,6 @@
date.setInnerText(FormatUtil.mediumFormat(person.date()));
}
- private static String parenthesize(String str) {
- return new StringBuilder()
- .append("(")
- .append(str)
- .append(")")
- .toString();
- }
-
private static String renderName(GitPerson person) {
return person.name() + " <" + person.email() + ">";
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditFileAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditFileAction.java
index 8d31478..951c84d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditFileAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditFileAction.java
@@ -22,22 +22,22 @@
import com.google.gwtexpui.globalkey.client.GlobalKey;
import com.google.gwtexpui.user.client.PluginSafePopupPanel;
-public class EditFileAction {
+class EditFileAction {
private final PatchSet.Id id;
private final String content;
private final String file;
- private final String style;
+ private final ChangeScreen2.Style style;
private final Widget editMessageButton;
private final Widget relativeTo;
private EditFileBox editBox;
private PopupPanel popup;
- public EditFileAction(
+ EditFileAction(
PatchSet.Id id,
String content,
String file,
- String style,
+ ChangeScreen2.Style style,
Widget editButton,
Widget relativeTo) {
this.id = id;
@@ -62,10 +62,8 @@
}
final PluginSafePopupPanel p = new PluginSafePopupPanel(true);
- p.setStyleName(style);
- if (editMessageButton != null) {
- p.addAutoHidePartner(editMessageButton.getElement());
- }
+ p.setStyleName(style.replyBox());
+ p.addAutoHidePartner(editMessageButton.getElement());
p.addCloseHandler(new CloseHandler<PopupPanel>() {
@Override
public void onClose(CloseEvent<PopupPanel> event) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
index 8a61b15..d57a834 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
@@ -332,7 +332,7 @@
@Override
public void onSuccess(String result) {
EditFileAction edit = new EditFileAction(
- id, result, path, style.replyBox(), editButton, replyButton);
+ id, result, path, style, editButton, replyButton);
edit.onEdit();
}
});
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox2.java
index 40ef953..2650a1e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox2.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox2.java
@@ -52,7 +52,6 @@
interface BoxStyle extends CssResource {
String selected();
- String replyBox();
}
@UiField Image icon;
@@ -124,22 +123,8 @@
}
List<WebLinkInfo> webLinks = Natives.asList(meta.web_links());
if (webLinks != null) {
- for (WebLinkInfo weblink : webLinks) {
- Anchor a = new Anchor();
- a.setHref(weblink.url());
- if (weblink.target() != null && !weblink.target().isEmpty()) {
- a.setTarget(weblink.target());
- }
- if (weblink.imageUrl() != null && !weblink.imageUrl().isEmpty()) {
- Image img = new Image();
- img.setAltText(weblink.name());
- img.setUrl(weblink.imageUrl());
- img.setTitle(weblink.name());
- a.getElement().appendChild(img.getElement());
- } else {
- a.setText("(" + weblink.name() + ")");
- }
- linkPanel.add(a);
+ for (WebLinkInfo webLink : webLinks) {
+ linkPanel.add(webLink.toAnchor());
}
}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox2.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox2.ui.xml
index eba362a..ba7a0a7 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox2.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox2.ui.xml
@@ -56,10 +56,6 @@
.iconCell {
width: 16px;
}
- .replyBox {
- background-color: trimColor;
- z-index: 10;
- }
</ui:style>
<g:HTMLPanel>
<table class='{style.table}'>
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RequestUtil.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RequestUtil.java
index c47ca30..04aa44a 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RequestUtil.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RequestUtil.java
@@ -18,6 +18,18 @@
/** Utilities for manipulating HTTP request objects. */
public class RequestUtil {
+ /** HTTP request attribute for storing the Throwable that caused an error condition. */
+ private static final String ATTRIBUTE_ERROR_TRACE =
+ RequestUtil.class.getName() + "/ErrorTraceThrowable";
+
+ public static void setErrorTraceAttribute(HttpServletRequest req, Throwable t) {
+ req.setAttribute(ATTRIBUTE_ERROR_TRACE, t);
+ }
+
+ public static Throwable getErrorTraceAttribute(HttpServletRequest req) {
+ return (Throwable) req.getAttribute(ATTRIBUTE_ERROR_TRACE);
+ }
+
/**
* @return the same value as {@link HttpServletRequest#getPathInfo()}, but
* without decoding URL-encoded characters.
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RunAsFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RunAsFilter.java
index 3418354..614184a 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RunAsFilter.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RunAsFilter.java
@@ -14,11 +14,11 @@
package com.google.gerrit.httpd;
+import static com.google.gerrit.httpd.restapi.RestApiServlet.replyError;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gerrit.httpd.restapi.RestApiServlet;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountResolver;
@@ -77,17 +77,19 @@
String runas = req.getHeader(RUN_AS);
if (runas != null) {
if (!enabled) {
- RestApiServlet.replyError(req, res,
+ replyError(req, res,
SC_FORBIDDEN,
- RUN_AS + " disabled by auth.enableRunAs = false");
+ RUN_AS + " disabled by auth.enableRunAs = false",
+ null);
return;
}
CurrentUser self = session.get().getCurrentUser();
if (!self.getCapabilities().canRunAs()) {
- RestApiServlet.replyError(req, res,
+ replyError(req, res,
SC_FORBIDDEN,
- "not permitted to use " + RUN_AS);
+ "not permitted to use " + RUN_AS,
+ null);
return;
}
@@ -96,15 +98,17 @@
target = accountResolver.find(runas);
} catch (OrmException e) {
log.warn("cannot resolve account for " + RUN_AS, e);
- RestApiServlet.replyError(req, res,
+ replyError(req, res,
SC_INTERNAL_SERVER_ERROR,
- "cannot resolve " + RUN_AS);
+ "cannot resolve " + RUN_AS,
+ e);
return;
}
if (target == null) {
- RestApiServlet.replyError(req, res,
+ replyError(req, res,
SC_FORBIDDEN,
- "no account matches " + RUN_AS);
+ "no account matches " + RUN_AS,
+ null);
return;
}
session.get().setUserAccountId(target.getId());
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/ParameterParser.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/ParameterParser.java
index 9183d5c..46843fc 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/ParameterParser.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/ParameterParser.java
@@ -68,7 +68,7 @@
clp.parseOptionMap(in);
} catch (CmdLineException e) {
if (!clp.wasHelpRequestedByOption()) {
- replyError(req, res, SC_BAD_REQUEST, e.getMessage());
+ replyError(req, res, SC_BAD_REQUEST, e.getMessage(), e);
return false;
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 979990a..4fb6e24 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -345,27 +345,27 @@
}
}
} catch (AuthException e) {
- replyError(req, res, status = SC_FORBIDDEN, e.getMessage(), e.caching());
+ replyError(req, res, status = SC_FORBIDDEN, e.getMessage(), e.caching(), e);
} catch (BadRequestException e) {
- replyError(req, res, status = SC_BAD_REQUEST, e.getMessage(), e.caching());
+ replyError(req, res, status = SC_BAD_REQUEST, e.getMessage(), e.caching(), e);
} catch (MethodNotAllowedException e) {
- replyError(req, res, status = SC_METHOD_NOT_ALLOWED, "Method not allowed", e.caching());
+ replyError(req, res, status = SC_METHOD_NOT_ALLOWED, "Method not allowed", e.caching(), e);
} catch (ResourceConflictException e) {
- replyError(req, res, status = SC_CONFLICT, e.getMessage(), e.caching());
+ replyError(req, res, status = SC_CONFLICT, e.getMessage(), e.caching(), e);
} catch (PreconditionFailedException e) {
replyError(req, res, status = SC_PRECONDITION_FAILED,
- MoreObjects.firstNonNull(e.getMessage(), "Precondition failed"), e.caching());
+ MoreObjects.firstNonNull(e.getMessage(), "Precondition failed"), e.caching(), e);
} catch (ResourceNotFoundException e) {
- replyError(req, res, status = SC_NOT_FOUND, "Not found", e.caching());
+ replyError(req, res, status = SC_NOT_FOUND, "Not found", e.caching(), e);
} catch (UnprocessableEntityException e) {
replyError(req, res, status = 422,
- MoreObjects.firstNonNull(e.getMessage(), "Unprocessable Entity"), e.caching());
+ MoreObjects.firstNonNull(e.getMessage(), "Unprocessable Entity"), e.caching(), e);
} catch (AmbiguousViewException e) {
- replyError(req, res, status = SC_NOT_FOUND, e.getMessage());
+ replyError(req, res, status = SC_NOT_FOUND, e.getMessage(), e);
} catch (MalformedJsonException e) {
- replyError(req, res, status = SC_BAD_REQUEST, "Invalid " + JSON_TYPE + " in request");
+ replyError(req, res, status = SC_BAD_REQUEST, "Invalid " + JSON_TYPE + " in request", e);
} catch (JsonParseException e) {
- replyError(req, res, status = SC_BAD_REQUEST, "Invalid " + JSON_TYPE + " in request");
+ replyError(req, res, status = SC_BAD_REQUEST, "Invalid " + JSON_TYPE + " in request", e);
} catch (Exception e) {
status = SC_INTERNAL_SERVER_ERROR;
handleException(e, req, res);
@@ -928,21 +928,24 @@
if (!res.isCommitted()) {
res.reset();
- replyError(req, res, SC_INTERNAL_SERVER_ERROR, "Internal server error");
+ replyError(req, res, SC_INTERNAL_SERVER_ERROR, "Internal server error", err);
}
}
- public static void replyError(HttpServletRequest req,
- HttpServletResponse res, int statusCode, String msg) throws IOException {
- replyError(req, res, statusCode, msg, CacheControl.NONE);
+ public static void replyError(HttpServletRequest req, HttpServletResponse res,
+ int statusCode, String msg, @Nullable Throwable err) throws IOException {
+ replyError(req, res, statusCode, msg, CacheControl.NONE, err);
}
public static void replyError(HttpServletRequest req,
HttpServletResponse res, int statusCode, String msg,
- CacheControl c) throws IOException {
+ CacheControl c, @Nullable Throwable err) throws IOException {
res.setStatus(statusCode);
configureCaching(req, res, null, c);
replyText(req, res, msg);
+ if (err != null) {
+ RequestUtil.setErrorTraceAttribute(req, err);
+ }
}
static void replyText(@Nullable HttpServletRequest req,
diff --git a/gerrit-plugin-api/BUCK b/gerrit-plugin-api/BUCK
index 7147b1c..80ab9a3 100644
--- a/gerrit-plugin-api/BUCK
+++ b/gerrit-plugin-api/BUCK
@@ -56,7 +56,7 @@
java_doc(
name = 'plugin-api-javadoc',
title = 'Gerrit Review Plugin API Documentation',
- pkg = 'com.google.gerrit',
+ pkgs = ['com.google.gerrit'],
paths = [n for n in SRCS],
srcs = glob([n + '**/*.java' for n in SRCS]),
deps = [
diff --git a/gerrit-plugin-gwtui/BUCK b/gerrit-plugin-gwtui/BUCK
index 419176d..07e475b 100644
--- a/gerrit-plugin-gwtui/BUCK
+++ b/gerrit-plugin-gwtui/BUCK
@@ -1,4 +1,5 @@
COMMON = ['gerrit-gwtui-common/src/main/java/']
+GWTEXPUI = ['gerrit-gwtexpui/src/main/java/']
SRC = 'src/main/java/com/google/gerrit/'
SRCS = glob([SRC + '**/*.java'])
@@ -26,7 +27,13 @@
name = 'gwtui-api-lib2',
srcs = SRCS,
resources = glob(['src/main/**/*']),
- exported_deps = ['//gerrit-gwtui-common:client-lib2'],
+ exported_deps = [
+ '//gerrit-gwtexpui:Clippy',
+ '//gerrit-gwtexpui:GlobalKey',
+ '//gerrit-gwtexpui:SafeHtml',
+ '//gerrit-gwtexpui:UserAgent',
+ '//gerrit-gwtui-common:client-lib2',
+ ],
provided_deps = DEPS,
visibility = ['PUBLIC'],
)
@@ -35,6 +42,7 @@
name = 'gwtui-api-src',
deps = [
':gwtui-api-src-lib',
+ '//gerrit-gwtexpui:client-src-lib',
'//gerrit-gwtui-common:client-src-lib',
],
visibility = ['PUBLIC'],
@@ -50,9 +58,16 @@
java_doc(
name = 'gwtui-api-javadoc',
title = 'Gerrit Review GWT Extension API Documentation',
- pkg = 'com.google.gerrit',
- paths = ['src/main/java'] + COMMON,
+ pkgs = [
+ 'com.google.gerrit',
+ 'com.google.gwtexpui.clippy',
+ 'com.google.gwtexpui.globalkey',
+ 'com.google.gwtexpui.safehtml',
+ 'com.google.gwtexpui.user',
+ ],
+ paths = COMMON + GWTEXPUI,
srcs = SRCS,
deps = DEPS + ['//gerrit-gwtui-common:client-lib2'],
visibility = ['PUBLIC'],
+ do_it_wrong = True,
)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/WebLinks.java b/gerrit-server/src/main/java/com/google/gerrit/server/WebLinks.java
index 67cb45e..ddd59ec 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/WebLinks.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/WebLinks.java
@@ -50,10 +50,7 @@
public List<WebLinkInfo> getPatchSetLinks(String project, String commit) {
List<WebLinkInfo> links = new ArrayList<>(4);
for (PatchSetWebLink webLink : patchSetLinks) {
- links.add(new WebLinkInfo(webLink.getLinkName(),
- webLink.getImageUrl(),
- webLink.getPatchSetUrl(project, commit),
- webLink.getTarget()));
+ links.add(webLink.getPathSetWebLink(project, commit));
}
return links;
}
@@ -62,13 +59,9 @@
String file) {
List<WebLinkInfo> links = new ArrayList<>(4);
for (FileWebLink webLink : fileLinks) {
- String name = webLink.getLinkName();
- String url = webLink.getFileUrl(project, revision, file);
- if (!Strings.isNullOrEmpty(name) && !Strings.isNullOrEmpty(url)) {
- links.add(new WebLinkInfo(name,
- webLink.getImageUrl(),
- url,
- webLink.getTarget()));
+ WebLinkInfo info = webLink.getFileWebLink(project, revision, file);
+ if (!Strings.isNullOrEmpty(info.name) && !Strings.isNullOrEmpty(info.url)) {
+ links.add(info);
}
}
return links;
@@ -77,10 +70,7 @@
public Iterable<WebLinkInfo> getProjectLinks(String project) {
List<WebLinkInfo> links = Lists.newArrayList();
for (ProjectWebLink webLink : projectLinks) {
- links.add(new WebLinkInfo(webLink.getLinkName(),
- webLink.getImageUrl(),
- webLink.getProjectUrl(project),
- webLink.getTarget()));
+ links.add(webLink.getProjectWeblink(project));
}
return links;
}
@@ -88,10 +78,7 @@
public Iterable<WebLinkInfo> getBranchLinks(String project, String branch) {
List<WebLinkInfo> links = Lists.newArrayList();
for (BranchWebLink webLink : branchLinks) {
- links.add(new WebLinkInfo(webLink.getLinkName(),
- webLink.getImageUrl(),
- webLink.getBranchUrl(project, branch),
- webLink.getTarget()));
+ links.add(webLink.getBranchWebLink(project, branch));
}
return links;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java
index 58c674c..3c21d17 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java
@@ -63,7 +63,7 @@
public Response<SshKeyInfo> apply(AccountResource rsrc, Input input)
throws AuthException, BadRequestException, OrmException, IOException {
if (self.get() != rsrc.getUser()
- && !self.get().getCapabilities().canModifyAccount()) {
+ && !self.get().getCapabilities().canAdministrateServer()) {
throw new AuthException("not allowed to add SSH keys");
}
return apply(rsrc.getUser(), input);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java
index 631256a..2721057 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java
@@ -169,12 +169,6 @@
|| canAdministrateServer();
}
- /** @return true if the user can generate HTTP passwords for users other than self. */
- public boolean canGenerateHttpPassword() {
- return canPerform(GlobalCapability.GENERATE_HTTP_PASSWORD)
- || canAdministrateServer();
- }
-
/** @return true if the user can impersonate another user. */
public boolean canRunAs() {
return canPerform(GlobalCapability.RUN_AS);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java
index 7df1848..9066858 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java
@@ -14,9 +14,11 @@
package com.google.gerrit.server.account;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.DeleteSshKey.Input;
import com.google.gerrit.server.ssh.SshKeyCache;
import com.google.gwtorm.server.OrmException;
@@ -32,18 +34,26 @@
public static class Input {
}
+ private final Provider<CurrentUser> self;
private final Provider<ReviewDb> dbProvider;
private final SshKeyCache sshKeyCache;
@Inject
- DeleteSshKey(Provider<ReviewDb> dbProvider, SshKeyCache sshKeyCache) {
+ DeleteSshKey(Provider<ReviewDb> dbProvider,
+ Provider<CurrentUser> self,
+ SshKeyCache sshKeyCache) {
+ this.self = self;
this.dbProvider = dbProvider;
this.sshKeyCache = sshKeyCache;
}
@Override
public Response<?> apply(AccountResource.SshKey rsrc, Input input)
- throws OrmException {
+ throws AuthException, OrmException {
+ if (self.get() != rsrc.getUser()
+ && !self.get().getCapabilities().canAdministrateServer()) {
+ throw new AuthException("not allowed to delete SSH keys");
+ }
dbProvider.get().accountSshKeys()
.deleteKeys(Collections.singleton(rsrc.getSshKey().getKey()));
sshKeyCache.evict(rsrc.getUser().getUserName());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java
index 4ac65ec..43b76e2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java
@@ -20,7 +20,6 @@
import static com.google.gerrit.common.data.GlobalCapability.CREATE_PROJECT;
import static com.google.gerrit.common.data.GlobalCapability.EMAIL_REVIEWERS;
import static com.google.gerrit.common.data.GlobalCapability.FLUSH_CACHES;
-import static com.google.gerrit.common.data.GlobalCapability.GENERATE_HTTP_PASSWORD;
import static com.google.gerrit.common.data.GlobalCapability.KILL_TASK;
import static com.google.gerrit.common.data.GlobalCapability.MODIFY_ACCOUNT;
import static com.google.gerrit.common.data.GlobalCapability.PRIORITY;
@@ -115,7 +114,6 @@
have.put(CREATE_PROJECT, cc.canCreateProject());
have.put(EMAIL_REVIEWERS, cc.canEmailReviewers());
have.put(FLUSH_CACHES, cc.canFlushCaches());
- have.put(GENERATE_HTTP_PASSWORD, cc.canGenerateHttpPassword());
have.put(KILL_TASK, cc.canKillTask());
have.put(MODIFY_ACCOUNT, cc.canModifyAccount());
have.put(RUN_GC, cc.canRunGC());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetHttpPassword.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetHttpPassword.java
index e080015..c49ab98 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetHttpPassword.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetHttpPassword.java
@@ -36,7 +36,7 @@
public String apply(AccountResource rsrc) throws AuthException,
ResourceNotFoundException {
if (self.get() != rsrc.getUser()
- && !self.get().getCapabilities().canGenerateHttpPassword()) {
+ && !self.get().getCapabilities().canAdministrateServer()) {
throw new AuthException("not allowed to get http password");
}
AccountState s = rsrc.getUser().state();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java
index 93b35c6..c6b6282 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java
@@ -79,19 +79,19 @@
String newPassword;
if (input.generate) {
if (self.get() != rsrc.getUser()
- && !self.get().getCapabilities().canGenerateHttpPassword()) {
+ && !self.get().getCapabilities().canAdministrateServer()) {
throw new AuthException("not allowed to generate HTTP password");
}
newPassword = generate();
} else if (input.httpPassword == null) {
if (self.get() != rsrc.getUser()
- && !self.get().getCapabilities().canGenerateHttpPassword()) {
+ && !self.get().getCapabilities().canAdministrateServer()) {
throw new AuthException("not allowed to clear HTTP password");
}
newPassword = null;
} else {
- if (!self.get().getCapabilities().canGenerateHttpPassword()) {
+ if (!self.get().getCapabilities().canAdministrateServer()) {
throw new AuthException("not allowed to set HTTP password directly, "
+ "requires the Generate HTTP Password permission");
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java
index 8375256..1a2c6bd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java
@@ -168,9 +168,11 @@
result.metaB.name = ps.getNewName();
setContentType(result.metaB, state, ps.getFileModeB(), ps.getMimeTypeB());
result.metaB.lines = ps.getB().size();
- result.metaB.webLinks = getFileWebLinks(state.getProject(),
- resource.getRevision().getPatchSet().getRefName(),
- result.metaB.name);
+ String rev = resource.getRevision().getEdit().isPresent()
+ ? resource.getRevision().getEdit().get().getRefName()
+ : resource.getRevision().getPatchSet().getRefName();
+ result.metaB.webLinks =
+ getFileWebLinks(state.getProject(), rev, result.metaB.name);
}
if (intraline) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java
index 481e535..270f718 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java
@@ -35,7 +35,6 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.MergeabilityChecksExecutor.Priority;
-import com.google.gerrit.server.change.Mergeable.MergeableInfo;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.git.WorkQueue.Executor;
@@ -150,20 +149,18 @@
List<ListenableFuture<?>> result =
Lists.newArrayListWithCapacity(changes.size());
for (final Change c : changes) {
- ListenableFuture<Boolean> b =
- executor.submit(new Task(c, force));
+ // Don't try to guess whether Mergeable will reindex; just turn
+ // off reindexing in that code path and do it explicitly below.
+ ListenableFuture<Void> b =
+ executor.submit(new Task(c, force, !reindex));
if (reindex) {
result.add(Futures.transform(
- b, new AsyncFunction<Boolean, Object>() {
+ b, new AsyncFunction<Void, Void>() {
@SuppressWarnings("unchecked")
@Override
- public ListenableFuture<Object> apply(
- Boolean indexUpdated) throws Exception {
- if (!indexUpdated) {
- return (ListenableFuture<Object>)
- indexer.indexAsync(c.getId());
- }
- return Futures.immediateFuture(null);
+ public ListenableFuture<Void> apply(Void o) {
+ return (ListenableFuture<Void>)
+ indexer.indexAsync(c.getId());
}
}));
} else {
@@ -291,19 +288,21 @@
return ProjectConfig.read(metaDataUpdateFactory.create(p), id);
}
- private class Task implements Callable<Boolean> {
+ private class Task implements Callable<Void> {
private final Change change;
private final boolean force;
+ private final boolean reindex;
private ReviewDb reviewDb;
- Task(Change change, boolean force) {
+ Task(Change change, boolean force, boolean reindex) {
this.change = change;
this.force = force;
+ this.reindex = reindex;
}
@Override
- public Boolean call() throws Exception {
+ public Void call() throws Exception {
mergeabilityCheckQueue.updatingMergeabilityFlag(change, force);
RequestContext context = new RequestContext() {
@@ -335,20 +334,20 @@
PatchSet ps = db.patchSets().get(change.currentPatchSetId());
if (ps == null) {
// Cannot compute mergeability if current patch set is missing.
- return false;
+ return null;
}
Mergeable m = mergeable.get();
m.setForce(force);
+ m.setReindex(reindex);
ChangeControl control =
- changeControlFactory.controlFor(change.getId(), context.getCurrentUser());
- MergeableInfo info = m.apply(
- new RevisionResource(new ChangeResource(control), ps));
- return change.isMergeable() != info.mergeable;
+ changeControlFactory.controlFor(change, context.getCurrentUser());
+ m.apply(new RevisionResource(new ChangeResource(control), ps));
+ return null;
} catch (ResourceConflictException e) {
// change is closed
- return false;
+ return null;
} catch (Exception e) {
log.error(String.format(
"cannot update mergeability flag of change %d in project %s after update of %s",
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
index f151792..3605868 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
@@ -82,6 +82,10 @@
this.force = force;
}
+ public void setReindex(boolean reindex) {
+ this.reindex = reindex;
+ }
+
private final GitRepositoryManager gitManager;
private final ProjectCache projectCache;
private final ChangeData.Factory changeDataFactory;
@@ -90,6 +94,7 @@
private final ChangeIndexer indexer;
private boolean force;
+ private boolean reindex;
@Inject
Mergeable(GitRepositoryManager gitManager,
@@ -104,6 +109,7 @@
this.submitStrategyFactory = submitStrategyFactory;
this.db = db;
this.indexer = indexer;
+ reindex = true;
}
@Override
@@ -197,7 +203,7 @@
}
}
});
- if (c != null) {
+ if (reindex && c != null) {
indexer.index(db.get(), c);
}
return mergeable;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java
index 6322668..295189b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java
@@ -68,7 +68,8 @@
}
try {
- rebaseChange.get().rebase(rsrc.getPatchSet().getId(), rsrc.getUser());
+ rebaseChange.get().rebase(rsrc.getChange(), rsrc.getPatchSet().getId(),
+ rsrc.getUser());
} catch (InvalidChangeOperationException e) {
throw new ResourceConflictException(e.getMessage());
} catch (IOException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java
index 1e9f94a..d3869ad 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java
@@ -95,6 +95,7 @@
* E-mail notification and triggering of hooks happens for the creation of the
* new patch set.
*
+ * @param change the change to perform the rebase for
* @param patchSetId the id of the patch set
* @param uploader the user that creates the rebased patch set
* @throws NoSuchChangeException thrown if the change to which the patch set
@@ -105,18 +106,17 @@
* @throws IOException thrown if rebase is not possible or not needed
* @throws InvalidChangeOperationException thrown if rebase is not allowed
*/
- public void rebase(final PatchSet.Id patchSetId, final IdentifiedUser uploader)
+ public void rebase(Change change, PatchSet.Id patchSetId, final IdentifiedUser uploader)
throws NoSuchChangeException, EmailException, OrmException, IOException,
InvalidChangeOperationException {
final Change.Id changeId = patchSetId.getParentKey();
final ChangeControl changeControl =
- changeControlFactory.validateFor(changeId, uploader);
+ changeControlFactory.validateFor(change, uploader);
if (!changeControl.canRebase()) {
throw new InvalidChangeOperationException(
"Cannot rebase: New patch sets are not allowed to be added to change: "
+ changeId.toString());
}
- final Change change = changeControl.getChange();
Repository git = null;
RevWalk rw = null;
ObjectInserter inserter = null;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/CapabilityConstants.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/CapabilityConstants.java
index 3805a0e..4c93ce0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/CapabilityConstants.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/CapabilityConstants.java
@@ -29,7 +29,6 @@
public String createProject;
public String emailReviewers;
public String flushCaches;
- public String generateHttpPassword;
public String killTask;
public String modifyAccount;
public String priority;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeAbandonedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeAbandonedEvent.java
index b0eb9c6..33cf241 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeAbandonedEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeAbandonedEvent.java
@@ -14,14 +14,38 @@
package com.google.gerrit.server.events;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.PatchSetAttribute;
public class ChangeAbandonedEvent extends ChangeEvent {
- public final String type = "change-abandoned";
- public ChangeAttribute change;
- public PatchSetAttribute patchSet;
- public AccountAttribute abandoner;
- public String reason;
+ public final String type = "change-abandoned";
+ public ChangeAttribute change;
+ public PatchSetAttribute patchSet;
+ public AccountAttribute abandoner;
+ public String reason;
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return new Project.NameKey(change.project);
+ }
+
+ @Override
+ public Change.Key getChangeKey() {
+ return new Change.Key(change.id);
+ }
+
+ @Override
+ public String getRefName() {
+ return R_HEADS + change.branch;
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeEvent.java
index 904a0a0..77ba756 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeEvent.java
@@ -14,5 +14,15 @@
package com.google.gerrit.server.events;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
+
public abstract class ChangeEvent {
+ public abstract String getType();
+
+ public abstract Project.NameKey getProjectNameKey();
+
+ public abstract Change.Key getChangeKey();
+
+ public abstract String getRefName();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeMergedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeMergedEvent.java
index 38996a5..f1aaa0a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeMergedEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeMergedEvent.java
@@ -14,13 +14,37 @@
package com.google.gerrit.server.events;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.PatchSetAttribute;
public class ChangeMergedEvent extends ChangeEvent {
- public final String type = "change-merged";
- public ChangeAttribute change;
- public PatchSetAttribute patchSet;
- public AccountAttribute submitter;
+ public final String type = "change-merged";
+ public ChangeAttribute change;
+ public PatchSetAttribute patchSet;
+ public AccountAttribute submitter;
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return new Project.NameKey(change.project);
+ }
+
+ @Override
+ public Change.Key getChangeKey() {
+ return new Change.Key(change.id);
+ }
+
+ @Override
+ public String getRefName() {
+ return R_HEADS + change.branch;
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeRestoredEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeRestoredEvent.java
index e761190..bf759249 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeRestoredEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeRestoredEvent.java
@@ -14,14 +14,38 @@
package com.google.gerrit.server.events;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.PatchSetAttribute;
public class ChangeRestoredEvent extends ChangeEvent {
- public final String type = "change-restored";
- public ChangeAttribute change;
- public PatchSetAttribute patchSet;
- public AccountAttribute restorer;
- public String reason;
+ public final String type = "change-restored";
+ public ChangeAttribute change;
+ public PatchSetAttribute patchSet;
+ public AccountAttribute restorer;
+ public String reason;
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return new Project.NameKey(change.project);
+ }
+
+ @Override
+ public Change.Key getChangeKey() {
+ return new Change.Key(change.id);
+ }
+
+ @Override
+ public String getRefName() {
+ return R_HEADS + change.branch;
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/CommentAddedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/CommentAddedEvent.java
index 52d7409..3b0cf9c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/CommentAddedEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/CommentAddedEvent.java
@@ -14,16 +14,40 @@
package com.google.gerrit.server.events;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ApprovalAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.PatchSetAttribute;
public class CommentAddedEvent extends ChangeEvent {
- public final String type = "comment-added";
- public ChangeAttribute change;
- public PatchSetAttribute patchSet;
- public AccountAttribute author;
- public ApprovalAttribute[] approvals;
- public String comment;
+ public final String type = "comment-added";
+ public ChangeAttribute change;
+ public PatchSetAttribute patchSet;
+ public AccountAttribute author;
+ public ApprovalAttribute[] approvals;
+ public String comment;
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return new Project.NameKey(change.project);
+ }
+
+ @Override
+ public Change.Key getChangeKey() {
+ return new Change.Key(change.id);
+ }
+
+ @Override
+ public String getRefName() {
+ return R_HEADS + change.branch;
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/CommitReceivedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/CommitReceivedEvent.java
index 8dd1084..825b595 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/CommitReceivedEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/CommitReceivedEvent.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.events;
+import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.IdentifiedUser;
@@ -21,6 +22,7 @@
import org.eclipse.jgit.transport.ReceiveCommand;
public class CommitReceivedEvent extends ChangeEvent {
+ public final String type = "commit-received";
public final ReceiveCommand command;
public final Project project;
public final String refName;
@@ -35,4 +37,24 @@
this.commit = commit;
this.user = user;
}
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return project.getNameKey();
+ }
+
+ @Override
+ public Change.Key getChangeKey() {
+ return null;
+ }
+
+ @Override
+ public String getRefName() {
+ return refName;
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/DraftPublishedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/DraftPublishedEvent.java
index 7fd033a..a595165 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/DraftPublishedEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/DraftPublishedEvent.java
@@ -14,13 +14,37 @@
package com.google.gerrit.server.events;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.PatchSetAttribute;
public class DraftPublishedEvent extends ChangeEvent {
- public final String type = "draft-published";
- public ChangeAttribute change;
- public PatchSetAttribute patchSet;
- public AccountAttribute uploader;
+ public final String type = "draft-published";
+ public ChangeAttribute change;
+ public PatchSetAttribute patchSet;
+ public AccountAttribute uploader;
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return new Project.NameKey(change.project);
+ }
+
+ @Override
+ public Change.Key getChangeKey() {
+ return new Change.Key(change.id);
+ }
+
+ @Override
+ public String getRefName() {
+ return R_HEADS + change.branch;
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/HashtagsChangedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/HashtagsChangedEvent.java
index 4b4bbba..91c14b1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/HashtagsChangedEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/HashtagsChangedEvent.java
@@ -14,6 +14,10 @@
package com.google.gerrit.server.events;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
@@ -24,4 +28,24 @@
public String[] added;
public String[] removed;
public String[] hashtags;
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return new Project.NameKey(change.project);
+ }
+
+ @Override
+ public Change.Key getChangeKey() {
+ return new Change.Key(change.id);
+ }
+
+ @Override
+ public String getRefName() {
+ return R_HEADS + change.branch;
+ }
}
\ No newline at end of file
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/MergeFailedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/MergeFailedEvent.java
index 599fe60..4ec6da3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/MergeFailedEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/MergeFailedEvent.java
@@ -14,14 +14,38 @@
package com.google.gerrit.server.events;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.PatchSetAttribute;
public class MergeFailedEvent extends ChangeEvent {
- public final String type = "merge-failed";
- public ChangeAttribute change;
- public PatchSetAttribute patchSet;
- public AccountAttribute submitter;
- public String reason;
+ public final String type = "merge-failed";
+ public ChangeAttribute change;
+ public PatchSetAttribute patchSet;
+ public AccountAttribute submitter;
+ public String reason;
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return new Project.NameKey(change.project);
+ }
+
+ @Override
+ public Change.Key getChangeKey() {
+ return new Change.Key(change.id);
+ }
+
+ @Override
+ public String getRefName() {
+ return R_HEADS + change.branch;
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/PatchSetCreatedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/PatchSetCreatedEvent.java
index fbaf4ef..830c6cb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/PatchSetCreatedEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/PatchSetCreatedEvent.java
@@ -14,13 +14,37 @@
package com.google.gerrit.server.events;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.PatchSetAttribute;
public class PatchSetCreatedEvent extends ChangeEvent {
- public final String type = "patchset-created";
- public ChangeAttribute change;
- public PatchSetAttribute patchSet;
- public AccountAttribute uploader;
+ public final String type = "patchset-created";
+ public ChangeAttribute change;
+ public PatchSetAttribute patchSet;
+ public AccountAttribute uploader;
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return new Project.NameKey(change.project);
+ }
+
+ @Override
+ public Change.Key getChangeKey() {
+ return new Change.Key(change.id);
+ }
+
+ @Override
+ public String getRefName() {
+ return R_HEADS + change.branch;
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/RefOperationReceivedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/RefOperationReceivedEvent.java
index d26632b..da3a215 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/RefOperationReceivedEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/RefOperationReceivedEvent.java
@@ -13,6 +13,7 @@
// limitations under the License.
package com.google.gerrit.server.events;
+import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.IdentifiedUser;
@@ -23,4 +24,24 @@
public ReceiveCommand command;
public Project project;
public IdentifiedUser user;
-}
\ No newline at end of file
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return project.getNameKey();
+ }
+
+ @Override
+ public Change.Key getChangeKey() {
+ return null;
+ }
+
+ @Override
+ public String getRefName() {
+ return command.getRefName();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/RefUpdatedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/RefUpdatedEvent.java
index 944c9ad..4b7244e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/RefUpdatedEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/RefUpdatedEvent.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.events;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.RefUpdateAttribute;
@@ -21,4 +23,24 @@
public final String type = "ref-updated";
public AccountAttribute submitter;
public RefUpdateAttribute refUpdate;
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return new Project.NameKey(refUpdate.project);
+ }
+
+ @Override
+ public Change.Key getChangeKey() {
+ return null;
+ }
+
+ @Override
+ public String getRefName() {
+ return refUpdate.refName;
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ReviewerAddedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ReviewerAddedEvent.java
index e00cc60..20eb30b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/ReviewerAddedEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ReviewerAddedEvent.java
@@ -14,13 +14,37 @@
package com.google.gerrit.server.events;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.PatchSetAttribute;
public class ReviewerAddedEvent extends ChangeEvent {
- public final String type = "reviewer-added";
- public ChangeAttribute change;
- public PatchSetAttribute patchSet;
- public AccountAttribute reviewer;
+ public final String type = "reviewer-added";
+ public ChangeAttribute change;
+ public PatchSetAttribute patchSet;
+ public AccountAttribute reviewer;
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return new Project.NameKey(change.project);
+ }
+
+ @Override
+ public Change.Key getChangeKey() {
+ return new Change.Key(change.id);
+ }
+
+ @Override
+ public String getRefName() {
+ return R_HEADS + change.branch;
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/TopicChangedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/TopicChangedEvent.java
index e725eac..f897dbc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/TopicChangedEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/TopicChangedEvent.java
@@ -14,6 +14,10 @@
package com.google.gerrit.server.events;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
@@ -22,4 +26,24 @@
public ChangeAttribute change;
public AccountAttribute changer;
public String oldTopic;
-}
\ No newline at end of file
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return new Project.NameKey(change.project);
+ }
+
+ @Override
+ public Change.Key getChangeKey() {
+ return new Change.Key(change.id);
+ }
+
+ @Override
+ public String getRefName() {
+ return R_HEADS + change.branch;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
index e5c5872..6db0cfa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -86,6 +86,7 @@
import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.joda.time.format.ISODateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -129,29 +130,39 @@
private static final long MAX_SUBMIT_WINDOW =
MILLISECONDS.convert(12, HOURS);
- private final GitRepositoryManager repoManager;
- private final SchemaFactory<ReviewDb> schemaFactory;
- private final ChangeNotes.Factory notesFactory;
- private final ProjectCache projectCache;
- private final GitReferenceUpdated gitRefUpdated;
- private final MergedSender.Factory mergedSenderFactory;
- private final MergeFailSender.Factory mergeFailSenderFactory;
- private final PatchSetInfoFactory patchSetInfoFactory;
- private final IdentifiedUser.GenericFactory identifiedUserFactory;
+ private final AccountCache accountCache;
+ private final ApprovalsUtil approvalsUtil;
private final ChangeControl.GenericFactory changeControlFactory;
private final ChangeData.Factory changeDataFactory;
+ private final ChangeHooks hooks;
+ private final ChangeIndexer indexer;
+ private final ChangeMessagesUtil cmUtil;
+ private final ChangeNotes.Factory notesFactory;
private final ChangeUpdate.Factory updateFactory;
+ private final GitReferenceUpdated gitRefUpdated;
+ private final GitRepositoryManager repoManager;
+ private final IdentifiedUser.GenericFactory identifiedUserFactory;
+ private final MergedSender.Factory mergedSenderFactory;
+ private final MergeFailSender.Factory mergeFailSenderFactory;
private final MergeQueue mergeQueue;
private final MergeValidators.Factory mergeValidatorsFactory;
- private final ApprovalsUtil approvalsUtil;
- private final ChangeMessagesUtil cmUtil;
+ private final PatchSetInfoFactory patchSetInfoFactory;
+ private final ProjectCache projectCache;
+ private final RequestScopePropagator requestScopePropagator;
+ private final SchemaFactory<ReviewDb> schemaFactory;
+ private final SubmitStrategyFactory submitStrategyFactory;
+ private final SubmoduleOp.Factory subOpFactory;
+ private final TagCache tagCache;
+ private final WorkQueue workQueue;
+ private final String logPrefix;
private final Branch.NameKey destBranch;
- private ProjectState destProject;
private final ListMultimap<SubmitType, CodeReviewCommit> toMerge;
private final List<CodeReviewCommit> potentiallyStillSubmittable;
private final Map<Change.Id, CodeReviewCommit> commits;
private final List<Change> toUpdate;
+
+ private ProjectState destProject;
private ReviewDb db;
private Repository repo;
private RevWalk rw;
@@ -161,60 +172,58 @@
private ObjectInserter inserter;
private PersonIdent refLogIdent;
- private final ChangeHooks hooks;
- private final AccountCache accountCache;
- private final TagCache tagCache;
- private final SubmitStrategyFactory submitStrategyFactory;
- private final SubmoduleOp.Factory subOpFactory;
- private final WorkQueue workQueue;
- private final RequestScopePropagator requestScopePropagator;
- private final ChangeIndexer indexer;
-
@Inject
- MergeOp(final GitRepositoryManager grm, final SchemaFactory<ReviewDb> sf,
- final ChangeNotes.Factory nf,
- final ProjectCache pc,
- final GitReferenceUpdated gru, final MergedSender.Factory msf,
- final MergeFailSender.Factory mfsf,
- final PatchSetInfoFactory psif, final IdentifiedUser.GenericFactory iuf,
- final ChangeControl.GenericFactory changeControlFactory,
- final ChangeData.Factory changeDataFactory,
- final ChangeUpdate.Factory updateFactory,
- final MergeQueue mergeQueue, @Assisted final Branch.NameKey branch,
- final ChangeHooks hooks, final AccountCache accountCache,
- final TagCache tagCache,
- final SubmitStrategyFactory submitStrategyFactory,
- final SubmoduleOp.Factory subOpFactory,
- final WorkQueue workQueue,
- final RequestScopePropagator requestScopePropagator,
- final ChangeIndexer indexer,
- final MergeValidators.Factory mergeValidatorsFactory,
- final ApprovalsUtil approvalsUtil,
- final ChangeMessagesUtil cmUtil) {
- repoManager = grm;
- schemaFactory = sf;
- notesFactory = nf;
- projectCache = pc;
- gitRefUpdated = gru;
- mergedSenderFactory = msf;
- mergeFailSenderFactory = mfsf;
- patchSetInfoFactory = psif;
- identifiedUserFactory = iuf;
+ MergeOp(AccountCache accountCache,
+ ApprovalsUtil approvalsUtil,
+ ChangeControl.GenericFactory changeControlFactory,
+ ChangeData.Factory changeDataFactory,
+ ChangeHooks hooks,
+ ChangeIndexer indexer,
+ ChangeMessagesUtil cmUtil,
+ ChangeNotes.Factory notesFactory,
+ ChangeUpdate.Factory updateFactory,
+ GitReferenceUpdated gitRefUpdated,
+ GitRepositoryManager repoManager,
+ IdentifiedUser.GenericFactory identifiedUserFactory,
+ MergedSender.Factory mergedSenderFactory,
+ MergeFailSender.Factory mergeFailSenderFactory,
+ MergeQueue mergeQueue,
+ MergeValidators.Factory mergeValidatorsFactory,
+ PatchSetInfoFactory patchSetInfoFactory,
+ ProjectCache projectCache,
+ RequestScopePropagator requestScopePropagator,
+ SchemaFactory<ReviewDb> schemaFactory,
+ SubmitStrategyFactory submitStrategyFactory,
+ SubmoduleOp.Factory subOpFactory,
+ TagCache tagCache,
+ WorkQueue workQueue,
+ @Assisted Branch.NameKey branch) {
+ this.accountCache = accountCache;
+ this.approvalsUtil = approvalsUtil;
this.changeControlFactory = changeControlFactory;
this.changeDataFactory = changeDataFactory;
- this.updateFactory = updateFactory;
- this.mergeQueue = mergeQueue;
this.hooks = hooks;
- this.accountCache = accountCache;
- this.tagCache = tagCache;
+ this.indexer = indexer;
+ this.cmUtil = cmUtil;
+ this.notesFactory = notesFactory;
+ this.updateFactory = updateFactory;
+ this.gitRefUpdated = gitRefUpdated;
+ this.repoManager = repoManager;
+ this.identifiedUserFactory = identifiedUserFactory;
+ this.mergedSenderFactory = mergedSenderFactory;
+ this.mergeFailSenderFactory = mergeFailSenderFactory;
+ this.mergeQueue = mergeQueue;
+ this.mergeValidatorsFactory = mergeValidatorsFactory;
+ this.patchSetInfoFactory = patchSetInfoFactory;
+ this.projectCache = projectCache;
+ this.requestScopePropagator = requestScopePropagator;
+ this.schemaFactory = schemaFactory;
this.submitStrategyFactory = submitStrategyFactory;
this.subOpFactory = subOpFactory;
+ this.tagCache = tagCache;
this.workQueue = workQueue;
- this.requestScopePropagator = requestScopePropagator;
- this.indexer = indexer;
- this.mergeValidatorsFactory = mergeValidatorsFactory;
- this.approvalsUtil = approvalsUtil;
- this.cmUtil = cmUtil;
+ logPrefix = String.format("[%s@%s]: ", branch.toString(),
+ ISODateTimeFormat.hourMinuteSecond().print(TimeUtil.nowMs()));
destBranch = branch;
toMerge = ArrayListMultimap.create();
potentiallyStillSubmittable = new ArrayList<>();
@@ -235,7 +244,9 @@
}
}
- public void merge() throws MergeException, NoSuchChangeException, IOException {
+ public void merge()
+ throws MergeException, NoSuchChangeException, IOException {
+ logDebug("Beginning merge attempt on {}", destBranch);
setDestProject();
try {
openSchema();
@@ -244,21 +255,23 @@
RefUpdate branchUpdate = openBranch();
boolean reopen = false;
- final ListMultimap<SubmitType, Change> toSubmit =
+ ListMultimap<SubmitType, Change> toSubmit =
validateChangeList(db.changes().submitted(destBranch).toList());
- final ListMultimap<SubmitType, CodeReviewCommit> toMergeNextTurn =
+ ListMultimap<SubmitType, CodeReviewCommit> toMergeNextTurn =
ArrayListMultimap.create();
- final List<CodeReviewCommit> potentiallyStillSubmittableOnNextRun =
+ List<CodeReviewCommit> potentiallyStillSubmittableOnNextRun =
new ArrayList<>();
while (!toMerge.isEmpty()) {
+ logDebug("Beginning merge iteration with {} left to merge",
+ toMerge.size());
toMergeNextTurn.clear();
- final Set<SubmitType> submitTypes =
- new HashSet<>(toMerge.keySet());
- for (final SubmitType submitType : submitTypes) {
+ Set<SubmitType> submitTypes = new HashSet<>(toMerge.keySet());
+ for (SubmitType submitType : submitTypes) {
if (reopen) {
+ logDebug("Reopening branch");
branchUpdate = openBranch();
}
- final SubmitStrategy strategy = createStrategy(submitType);
+ SubmitStrategy strategy = createStrategy(submitType);
preMerge(strategy, toMerge.get(submitType));
RefUpdate update = updateBranch(strategy, branchUpdate);
reopen = true;
@@ -269,40 +282,46 @@
fireRefUpdated(update);
}
- for (final Iterator<CodeReviewCommit> it =
+ for (Iterator<CodeReviewCommit> it =
potentiallyStillSubmittable.iterator(); it.hasNext();) {
- final CodeReviewCommit commit = it.next();
+ CodeReviewCommit commit = it.next();
if (containsMissingCommits(toMerge, commit)
|| containsMissingCommits(toMergeNextTurn, commit)) {
// change has missing dependencies, but all commits which are
// missing are still attempted to be merged with another submit
// strategy, retry to merge this commit in the next turn
+ logDebug("Revision {} of patch set {} has missing dependencies"
+ + " with different submit types, reconsidering on next run",
+ commit.name(), commit.getPatchsetId());
it.remove();
commit.setStatusCode(null);
commit.missing = null;
toMergeNextTurn.put(submitType, commit);
}
}
- potentiallyStillSubmittableOnNextRun.addAll(potentiallyStillSubmittable);
+ logDebug("Adding {} changes potentially submittable on next run",
+ potentiallyStillSubmittable.size());
+ potentiallyStillSubmittableOnNextRun.addAll(
+ potentiallyStillSubmittable);
potentiallyStillSubmittable.clear();
}
toMerge.clear();
toMerge.putAll(toMergeNextTurn);
+ logDebug("Adding {} changes to merge on next run", toMerge.size());
}
updateChangeStatus(toUpdate);
- for (final CodeReviewCommit commit : potentiallyStillSubmittableOnNextRun) {
- final Capable capable = isSubmitStillPossible(commit);
+ for (CodeReviewCommit commit : potentiallyStillSubmittableOnNextRun) {
+ Capable capable = isSubmitStillPossible(commit);
if (capable != Capable.OK) {
sendMergeFail(commit.notes(),
message(commit.change(), capable.getMessage()), false);
}
}
} catch (NoSuchProjectException noProject) {
- log.warn(String.format(
- "Project %s no longer exists, abandoning open changes",
- destBranch.getParentKey().get()));
+ logWarn("Project " + destBranch.getParentKey() + " no longer exists,"
+ + " abandoning open changes");
abandonAllOpenChanges();
} catch (OrmException e) {
throw new MergeException("Cannot query the database", e);
@@ -323,13 +342,12 @@
}
private boolean containsMissingCommits(
- final ListMultimap<SubmitType, CodeReviewCommit> map,
- final CodeReviewCommit commit) {
+ ListMultimap<SubmitType, CodeReviewCommit> map, CodeReviewCommit commit) {
if (!isSubmitForMissingCommitsStillPossible(commit)) {
return false;
}
- for (final CodeReviewCommit missingCommit : commit.missing) {
+ for (CodeReviewCommit missingCommit : commit.missing) {
if (!map.containsValue(missingCommit)) {
return false;
}
@@ -337,8 +355,12 @@
return true;
}
- private boolean isSubmitForMissingCommitsStillPossible(final CodeReviewCommit commit) {
+ private boolean isSubmitForMissingCommitsStillPossible(
+ CodeReviewCommit commit) {
+ PatchSet.Id psId = commit.getPatchsetId();
if (commit.missing == null || commit.missing.isEmpty()) {
+ logDebug("Patch set {} is not submittable: no list of missing commits",
+ psId);
return false;
}
@@ -346,7 +368,7 @@
try {
loadChangeInfo(missingCommit);
} catch (NoSuchChangeException | OrmException e) {
- log.error("Cannot check if missing commits can be submitted", e);
+ logError("Cannot check if missing commits can be submitted", e);
return false;
}
@@ -354,14 +376,21 @@
// The commit doesn't have a patch set, so it cannot be
// submitted to the branch.
//
+ logDebug("Patch set {} is not submittable: dependency {} has no"
+ + " associated patch set", psId, missingCommit.name());
return false;
}
if (!missingCommit.change().currentPatchSetId().equals(
missingCommit.getPatchsetId())) {
+ PatchSet.Id missingId = missingCommit.getPatchsetId();
// If the missing commit is not the current patch set,
// the change must be rebased to use the proper parent.
//
+ logDebug("Patch set {} is not submittable: depends on patch set {} of"
+ + " change {}, but current patch set is {}", psId, missingId,
+ missingId.getParentKey(),
+ missingCommit.change().currentPatchSetId());
return false;
}
}
@@ -369,27 +398,30 @@
return true;
}
- private void preMerge(final SubmitStrategy strategy,
- final List<CodeReviewCommit> toMerge) throws MergeException {
+ private void preMerge(SubmitStrategy strategy,
+ List<CodeReviewCommit> toMerge) throws MergeException {
+ logDebug("Running submit strategy {} for {} commits",
+ strategy.getClass().getSimpleName(), toMerge.size());
mergeTip = strategy.run(branchTip, toMerge);
refLogIdent = strategy.getRefLogIdent();
+ logDebug("Produced {} new commits", strategy.getNewCommits().size());
commits.putAll(strategy.getNewCommits());
}
- private SubmitStrategy createStrategy(final SubmitType submitType)
+ private SubmitStrategy createStrategy(SubmitType submitType)
throws MergeException, NoSuchProjectException {
return submitStrategyFactory.create(submitType, db, repo, rw, inserter,
canMergeFlag, getAlreadyAccepted(branchTip), destBranch);
}
private void openRepository() throws MergeException, NoSuchProjectException {
- final Project.NameKey name = destBranch.getParentKey();
+ Project.NameKey name = destBranch.getParentKey();
try {
repo = repoManager.openRepository(name);
} catch (RepositoryNotFoundException notFound) {
throw new NoSuchProjectException(name, notFound);
} catch (IOException err) {
- final String m = "Error opening repository \"" + name.get() + '"';
+ String m = "Error opening repository \"" + name.get() + '"';
throw new MergeException(m, err);
}
@@ -401,9 +433,10 @@
inserter = repo.newObjectInserter();
}
- private RefUpdate openBranch() throws MergeException, OrmException, NoSuchChangeException {
+ private RefUpdate openBranch()
+ throws MergeException, OrmException, NoSuchChangeException {
try {
- final RefUpdate branchUpdate = repo.updateRef(destBranch.get());
+ RefUpdate branchUpdate = repo.updateRef(destBranch.get());
if (branchUpdate.getOldObjectId() != null) {
branchTip =
(CodeReviewCommit) rw.parseCommit(branchUpdate.getOldObjectId());
@@ -411,7 +444,7 @@
branchTip = null;
branchUpdate.setExpectedOldObjectId(ObjectId.zeroId());
} else {
- for (final Change c : db.changes().submitted(destBranch).toList()) {
+ for (Change c : db.changes().submitted(destBranch).toList()) {
setNew(c, message(c, "Your change could not be merged, "
+ "because the destination branch does not exist anymore."));
}
@@ -422,50 +455,49 @@
}
}
- private Set<RevCommit> getAlreadyAccepted(final CodeReviewCommit branchTip)
+ private Set<RevCommit> getAlreadyAccepted(CodeReviewCommit branchTip)
throws MergeException {
- final Set<RevCommit> alreadyAccepted = new HashSet<>();
+ Set<RevCommit> alreadyAccepted = new HashSet<>();
if (branchTip != null) {
alreadyAccepted.add(branchTip);
}
try {
- for (final Ref r : repo.getRefDatabase().getRefs(ALL).values()) {
- if (r.getName().startsWith(Constants.R_HEADS)) {
- try {
- alreadyAccepted.add(rw.parseCommit(r.getObjectId()));
- } catch (IncorrectObjectTypeException iote) {
- // Not a commit? Skip over it.
- }
+ for (Ref r : repo.getRefDatabase().getRefs(Constants.R_HEADS).values()) {
+ try {
+ alreadyAccepted.add(rw.parseCommit(r.getObjectId()));
+ } catch (IncorrectObjectTypeException iote) {
+ // Not a commit? Skip over it.
}
}
} catch (IOException e) {
- throw new MergeException("Failed to determine already accepted commits.", e);
+ throw new MergeException(
+ "Failed to determine already accepted commits.", e);
}
+ logDebug("Found {} existing heads", alreadyAccepted.size());
return alreadyAccepted;
}
private ListMultimap<SubmitType, Change> validateChangeList(
- final List<Change> submitted) throws MergeException, NoSuchChangeException {
- final ListMultimap<SubmitType, Change> toSubmit =
- ArrayListMultimap.create();
+ List<Change> submitted) throws MergeException, NoSuchChangeException {
+ ListMultimap<SubmitType, Change> toSubmit = ArrayListMultimap.create();
- final Map<String, Ref> allRefs;
+ Map<String, Ref> allRefs;
try {
allRefs = repo.getRefDatabase().getRefs(ALL);
} catch (IOException e) {
throw new MergeException(e.getMessage(), e);
}
- final Set<ObjectId> tips = new HashSet<>();
- for (final Ref r : allRefs.values()) {
+ Set<ObjectId> tips = new HashSet<>();
+ for (Ref r : allRefs.values()) {
tips.add(r.getObjectId());
}
int commitOrder = 0;
- for (final Change chg : submitted) {
+ for (Change chg : submitted) {
ChangeControl ctl;
try {
ctl = changeControlFactory.controlFor(chg,
@@ -473,14 +505,15 @@
} catch (NoSuchChangeException e) {
throw new MergeException("Failed to validate changes", e);
}
- final Change.Id changeId = chg.getId();
+ Change.Id changeId = chg.getId();
if (chg.currentPatchSetId() == null) {
+ logError("Missing current patch set on change " + changeId);
commits.put(changeId, CodeReviewCommit.noPatchSet(ctl));
toUpdate.add(chg);
continue;
}
- final PatchSet ps;
+ PatchSet ps;
try {
ps = db.patchSets().get(chg.currentPatchSetId());
} catch (OrmException e) {
@@ -488,16 +521,18 @@
}
if (ps == null || ps.getRevision() == null
|| ps.getRevision().get() == null) {
+ logError("Missing patch set or revision on change " + changeId);
commits.put(changeId, CodeReviewCommit.noPatchSet(ctl));
toUpdate.add(chg);
continue;
}
- final String idstr = ps.getRevision().get();
- final ObjectId id;
+ String idstr = ps.getRevision().get();
+ ObjectId id;
try {
id = ObjectId.fromString(idstr);
} catch (IllegalArgumentException iae) {
+ logError("Invalid revision on patch set " + ps.getId());
commits.put(changeId, CodeReviewCommit.noPatchSet(ctl));
toUpdate.add(chg);
continue;
@@ -513,16 +548,19 @@
// want to merge the issue. We can't safely do that if the
// tip is not reachable.
//
+ logError("Revision " + idstr + " of patch set " + ps.getId()
+ + " is not contained in any ref");
commits.put(changeId, CodeReviewCommit.revisionGone(ctl));
toUpdate.add(chg);
continue;
}
- final CodeReviewCommit commit;
+ CodeReviewCommit commit;
try {
commit = (CodeReviewCommit) rw.parseCommit(id);
} catch (IOException e) {
- log.error("Invalid commit " + id.name() + " on " + chg.getKey(), e);
+ logError(
+ "Invalid commit " + idstr + " on patch set " + ps.getId(), e);
commits.put(changeId, CodeReviewCommit.revisionGone(ctl));
toUpdate.add(chg);
continue;
@@ -535,8 +573,11 @@
MergeValidators mergeValidators = mergeValidatorsFactory.create();
try {
- mergeValidators.validatePreMerge(repo, commit, destProject, destBranch, ps.getId());
+ mergeValidators.validatePreMerge(
+ repo, commit, destProject, destBranch, ps.getId());
} catch (MergeValidationException mve) {
+ logDebug("Revision {} of patch set {} failed validation: {}",
+ idstr, ps.getId(), mve.getStatus());
commit.setStatusCode(mve.getStatus());
toUpdate.add(chg);
continue;
@@ -549,11 +590,13 @@
//
try {
if (rw.isMergedInto(commit, branchTip)) {
+ logDebug("Revision {} of patch set {} is already merged",
+ idstr, ps.getId());
commit.setStatusCode(CommitMergeStatus.ALREADY_MERGED);
try {
setMerged(chg, null);
} catch (OrmException e) {
- log.error("Cannot mark change " + chg.getId() + " merged", e);
+ logError("Cannot mark change " + chg.getId() + " merged", e);
}
continue;
}
@@ -569,6 +612,8 @@
throw new MergeException("Cannot check submit type", err);
}
if (submitType == null) {
+ logError("No submit type for revision " + idstr + " of patch set "
+ + ps.getId());
commit.setStatusCode(CommitMergeStatus.NO_SUBMIT_TYPE);
toUpdate.add(chg);
continue;
@@ -588,24 +633,29 @@
SubmitTypeRecord r = new SubmitRuleEvaluator(cd).setPatchSet(ps)
.getSubmitType();
if (r.status != SubmitTypeRecord.Status.OK) {
- log.error("Failed to get submit type for " + ctl.getChange().getKey());
+ logError("Failed to get submit type for " + ctl.getChange().getKey());
return null;
}
return r.type;
} catch (OrmException e) {
- log.error("Failed to get submit type for " + ctl.getChange().getKey(), e);
+ logError("Failed to get submit type for " + ctl.getChange().getKey(), e);
return null;
}
}
- private RefUpdate updateBranch(final SubmitStrategy strategy,
- final RefUpdate branchUpdate) throws MergeException {
- if (branchTip == mergeTip || mergeTip == null) {
- // nothing to do
+ private RefUpdate updateBranch(SubmitStrategy strategy,
+ RefUpdate branchUpdate) throws MergeException {
+ if (branchTip == mergeTip) {
+ logDebug("Branch already at merge tip {}, no update to perform",
+ mergeTip.name());
+ return null;
+ } else if (mergeTip == null) {
+ logDebug("No merge tip, no update to perform");
return null;
}
if (RefNames.REFS_CONFIG.equals(branchUpdate.getName())) {
+ logDebug("Loading new configuration from {}", RefNames.REFS_CONFIG);
try {
ProjectConfig cfg =
new ProjectConfig(destProject.getProject().getNameKey());
@@ -622,7 +672,11 @@
branchUpdate.setNewObjectId(mergeTip);
branchUpdate.setRefLogMessage("merged", true);
try {
- switch (branchUpdate.update(rw)) {
+ RefUpdate.Result result = branchUpdate.update(rw);
+ logDebug("Update of {}: {}..{} returned status {}",
+ branchUpdate.getName(), branchUpdate.getOldObjectId(),
+ branchUpdate.getNewObjectId(), result);
+ switch (result) {
case NEW:
case FAST_FORWARD:
if (branchUpdate.getResult() == RefUpdate.Result.FAST_FORWARD) {
@@ -633,11 +687,11 @@
}
if (RefNames.REFS_CONFIG.equals(branchUpdate.getName())) {
- projectCache.evict(destProject.getProject());
- destProject = projectCache.get(destProject.getProject().getNameKey());
+ Project p = destProject.getProject();
+ projectCache.evict(p);
+ destProject = projectCache.get(p.getNameKey());
repoManager.setProjectDescription(
- destProject.getProject().getNameKey(),
- destProject.getProject().getDescription());
+ p.getNameKey(), p.getDescription());
}
return branchUpdate;
@@ -661,6 +715,7 @@
}
private void fireRefUpdated(RefUpdate branchUpdate) {
+ logDebug("Firing ref updated hooks for {}", branchUpdate.getName());
gitRefUpdated.fire(destBranch.getParentKey(), branchUpdate);
hooks.doRefUpdatedHook(destBranch, branchUpdate, getAccount(mergeTip));
}
@@ -683,18 +738,23 @@
return "";
}
- private void updateChangeStatus(final List<Change> submitted) throws NoSuchChangeException {
- for (final Change c : submitted) {
- final CodeReviewCommit commit = commits.get(c.getId());
- final CommitMergeStatus s = commit != null ? commit.getStatusCode() : null;
+ private void updateChangeStatus(List<Change> submitted)
+ throws NoSuchChangeException {
+ logDebug("Updating change status for {} changes", submitted);
+ for (Change c : submitted) {
+ CodeReviewCommit commit = commits.get(c.getId());
+ CommitMergeStatus s = commit != null ? commit.getStatusCode() : null;
if (s == null) {
// Shouldn't ever happen, but leave the change alone. We'll pick
// it up on the next pass.
//
+ logDebug("Submitted change {} did not appear in set of new commits"
+ + " produced by merge strategy", c.getId());
continue;
}
- final String txt = s.getMessage();
+ String txt = s.getMessage();
+ logDebug("Status of change {} on {}: {}", c.getId(), c.getDest(), s);
try {
switch (s) {
@@ -726,23 +786,27 @@
break;
case MISSING_DEPENDENCY:
+ logDebug("Change {} is missing dependency", c.getId());
potentiallyStillSubmittable.add(commit);
break;
default:
- setNew(commit, message(c, "Unspecified merge failure: " + s.name()));
+ setNew(commit,
+ message(c, "Unspecified merge failure: " + s.name()));
break;
}
} catch (OrmException err) {
- log.warn("Error updating change status for " + c.getId(), err);
+ logWarn("Error updating change status for " + c.getId(), err);
} catch (IOException err) {
- log.warn("Error updating change status for " + c.getId(), err);
+ logWarn("Error updating change status for " + c.getId(), err);
}
}
}
- private void updateSubscriptions(final List<Change> submitted) {
+ private void updateSubscriptions(List<Change> submitted) {
if (mergeTip != null && (branchTip == null || branchTip != mergeTip)) {
+ logDebug("Updating submodule subscriptions for {} changes",
+ submitted.size());
SubmoduleOp subOp =
subOpFactory.create(destBranch, mergeTip, rw, repo,
destProject.getProject(), submitted, commits,
@@ -750,30 +814,35 @@
try {
subOp.update();
} catch (SubmoduleException e) {
- log
- .error("The gitLinks were not updated according to the subscriptions "
- + e.getMessage());
+ logError(
+ "The gitLinks were not updated according to the subscriptions" , e);
}
}
}
- private Capable isSubmitStillPossible(final CodeReviewCommit commit) {
- final Capable capable;
- final Change c = commit.change();
- final boolean submitStillPossible = isSubmitForMissingCommitsStillPossible(commit);
- final long now = TimeUtil.nowMs();
- final long waitUntil = c.getLastUpdatedOn().getTime() + DEPENDENCY_DELAY;
+ private Capable isSubmitStillPossible(CodeReviewCommit commit) {
+ Capable capable;
+ Change c = commit.change();
+ boolean submitStillPossible =
+ isSubmitForMissingCommitsStillPossible(commit);
+ long now = TimeUtil.nowMs();
+ long waitUntil = c.getLastUpdatedOn().getTime() + DEPENDENCY_DELAY;
if (submitStillPossible && now < waitUntil) {
+ long recheckIn = waitUntil - now;
+ logDebug("Submit for {} is still possible; rechecking in {}ms",
+ c.getId(), recheckIn);
// If we waited a short while we might still be able to get
// this change submitted. Reschedule an attempt in a bit.
//
- mergeQueue.recheckAfter(destBranch, waitUntil - now, MILLISECONDS);
+ mergeQueue.recheckAfter(destBranch, recheckIn, MILLISECONDS);
capable = Capable.OK;
} else if (submitStillPossible) {
// It would be possible to submit the change if the missing
// dependencies are also submitted. Perhaps the user just
// forgot to submit those.
//
+ logDebug("Submit for {} is still possible after missing dependencies",
+ c.getId());
StringBuilder m = new StringBuilder();
m.append("Change could not be merged because of a missing dependency.");
m.append("\n");
@@ -793,20 +862,23 @@
// needs to rebase it in order to work around the missing
// dependencies.
//
+ logDebug("Submit for {} is not possible", c.getId());
StringBuilder m = new StringBuilder();
m.append("Change cannot be merged due to unsatisfiable dependencies.\n");
m.append("\n");
m.append("The following dependency errors were found:\n");
m.append("\n");
for (CodeReviewCommit missingCommit : commit.missing) {
- if (missingCommit.getPatchsetId() != null) {
+ PatchSet.Id missingPsId = missingCommit.getPatchsetId();
+ if (missingPsId != null) {
m.append("* Depends on patch set ");
- m.append(missingCommit.getPatchsetId().get());
+ m.append(missingPsId.get());
m.append(" of ");
m.append(missingCommit.change().getKey().abbreviate());
- if (missingCommit.getPatchsetId().get() != missingCommit.change().currentPatchSetId().get()) {
+ PatchSet.Id currPsId = missingCommit.change().currentPatchSetId();
+ if (!missingPsId.equals(currPsId)) {
m.append(", however the current patch set is ");
- m.append(missingCommit.change().currentPatchSetId().get());
+ m.append(currPsId.get());
}
m.append(".\n");
@@ -824,21 +896,21 @@
return capable;
}
- private void loadChangeInfo(final CodeReviewCommit commit)
+ private void loadChangeInfo(CodeReviewCommit commit)
throws NoSuchChangeException, OrmException {
if (commit.getControl() == null) {
List<PatchSet> matches =
db.patchSets().byRevision(new RevId(commit.name())).toList();
if (matches.size() == 1) {
- PatchSet ps = matches.get(0);
- commit.setPatchsetId(ps.getId());
- commit.setControl(changeControl(db.changes().get(ps.getId().getParentKey())));
+ PatchSet.Id psId = matches.get(0).getId();
+ commit.setPatchsetId(psId);
+ commit.setControl(changeControl(db.changes().get(psId.getParentKey())));
}
}
}
- private ChangeMessage message(final Change c, final String body) {
- final String uuid;
+ private ChangeMessage message(Change c, String body) {
+ String uuid;
try {
uuid = ChangeUtil.messageUUID(db);
} catch (OrmException e) {
@@ -852,6 +924,7 @@
private void setMerged(Change c, ChangeMessage msg)
throws OrmException, IOException, NoSuchChangeException {
+ logDebug("Setting change {} merged", c.getId());
ChangeUpdate update = null;
try {
db.changes().beginTransaction(c.getId());
@@ -882,7 +955,7 @@
accountCache.get(submitter.getAccountId()).getAccount(),
db.patchSets().get(merged), db);
} catch (OrmException ex) {
- log.error("Cannot run hook for submitted patch set " + c.getId(), ex);
+ logError("Cannot run hook for submitted patch set " + c.getId(), ex);
}
}
} finally {
@@ -909,7 +982,7 @@
try {
c.setCurrentPatchSet(patchSetInfoFactory.get(db, merged));
} catch (PatchSetInfoNotAvailableException e1) {
- log.error("Cannot read merged patch set " + merged, e1);
+ logError("Cannot read merged patch set " + merged, e1);
}
}
ChangeUtil.updated(c);
@@ -932,7 +1005,7 @@
reviewDb.close();
}
} catch (Exception e) {
- log.error("Cannot send email for submitted patch set " + c.getId(), e);
+ logError("Cannot send email for submitted patch set " + c.getId(), e);
return;
}
@@ -944,7 +1017,7 @@
cm.setPatchSet(patchSet);
cm.send();
} catch (Exception e) {
- log.error("Cannot send email for submitted patch set " + c.getId(), e);
+ logError("Cannot send email for submitted patch set " + c.getId(), e);
}
}
@@ -960,11 +1033,13 @@
c, identifiedUserFactory.create(c.getOwner()));
}
- private void setNew(CodeReviewCommit c, ChangeMessage msg) throws NoSuchChangeException, IOException {
+ private void setNew(CodeReviewCommit c, ChangeMessage msg)
+ throws NoSuchChangeException, IOException {
sendMergeFail(c.notes(), msg, true);
}
- private void setNew(Change c, ChangeMessage msg) throws OrmException, NoSuchChangeException, IOException {
+ private void setNew(Change c, ChangeMessage msg)
+ throws OrmException, NoSuchChangeException, IOException {
sendMergeFail(notesFactory.create(c), msg, true);
}
@@ -976,10 +1051,17 @@
@Nullable PatchSetApproval submitter,
ChangeMessage msg,
ChangeNotes notes) {
- if (submitter != null
- && TimeUtil.nowMs() - submitter.getGranted().getTime()
- > MAX_SUBMIT_WINDOW) {
- return RetryStatus.UNSUBMIT;
+ Change.Id id = notes.getChangeId();
+ if (submitter != null) {
+ long sinceMs = TimeUtil.nowMs() - submitter.getGranted().getTime();
+ if (sinceMs > MAX_SUBMIT_WINDOW) {
+ logDebug("Change {} submitted {}ms ago, unsubmitting", id, sinceMs);
+ return RetryStatus.UNSUBMIT;
+ } else {
+ logDebug("Change {} submitted {}ms ago, within window", id, sinceMs);
+ }
+ } else {
+ logDebug("No submitter for change {}", id);
}
try {
@@ -989,26 +1071,37 @@
&& Objects.equal(last.getMessage(), msg.getMessage())) {
long lastMs = last.getWrittenOn().getTime();
long msgMs = msg.getWrittenOn().getTime();
- return msgMs - lastMs > MAX_SUBMIT_WINDOW
- ? RetryStatus.UNSUBMIT
- : RetryStatus.RETRY_NO_MESSAGE;
+ long sinceMs = msgMs - lastMs;
+ if (sinceMs > MAX_SUBMIT_WINDOW) {
+ logDebug("Last message for change {} was {}ms ago, unsubmitting",
+ id, sinceMs);
+ return RetryStatus.UNSUBMIT;
+ } else {
+ logDebug("Last message for change {} was {}ms ago, within window",
+ id, sinceMs);
+ return RetryStatus.RETRY_NO_MESSAGE;
+ }
+ } else {
+ logDebug("Last message for change {} differed, adding message", id);
}
}
return RetryStatus.RETRY_ADD_MESSAGE;
} catch (OrmException err) {
- log.warn("Cannot check previous merge failure, unsubmitting", err);
+ logWarn("Cannot check previous merge failure, unsubmitting", err);
return RetryStatus.UNSUBMIT;
}
}
private void sendMergeFail(ChangeNotes notes, final ChangeMessage msg,
boolean makeNew) throws NoSuchChangeException, IOException {
+ logDebug("Possibly sending merge failure notification for {}",
+ notes.getChangeId());
PatchSetApproval submitter = null;
try {
submitter = approvalsUtil.getSubmitter(
db, notes, notes.getChange().currentPatchSetId());
} catch (Exception e) {
- log.error("Cannot get submitter", e);
+ logError("Cannot get submitter for change " + notes.getChangeId(), e);
}
if (!makeNew) {
@@ -1053,7 +1146,7 @@
db.rollback();
}
} catch (OrmException err) {
- log.warn("Cannot record merge failure message", err);
+ logWarn("Cannot record merge failure message", err);
}
if (update != null) {
update.commit();
@@ -1079,12 +1172,12 @@
reviewDb.close();
}
} catch (Exception e) {
- log.error("Cannot send email notifications about merge failure", e);
+ logError("Cannot send email notifications about merge failure", e);
return;
}
try {
- final MergeFailSender cm = mergeFailSenderFactory.create(c);
+ MergeFailSender cm = mergeFailSenderFactory.create(c);
if (from != null) {
cm.setFrom(from.getAccountId());
}
@@ -1092,7 +1185,7 @@
cm.setChangeMessage(msg);
cm.send();
} catch (Exception e) {
- log.error("Cannot send email notifications about merge failure", e);
+ logError("Cannot send email notifications about merge failure", e);
}
}
@@ -1106,7 +1199,7 @@
try {
indexFuture.checkedGet();
} catch (IOException e) {
- log.error("Failed to index new change message", e);
+ logError("Failed to index new change message", e);
}
}
@@ -1116,7 +1209,7 @@
accountCache.get(submitter.getAccountId()).getAccount(),
db.patchSets().get(c.currentPatchSetId()), msg.getMessage(), db);
} catch (OrmException ex) {
- log.error("Cannot run hook for merge failed " + c.getId(), ex);
+ logError("Cannot run hook for merge failed " + c.getId(), ex);
}
}
}
@@ -1125,7 +1218,8 @@
Exception err = null;
try {
openSchema();
- for (Change c : db.changes().byProjectOpenAll(destBranch.getParentKey())) {
+ for (Change c
+ : db.changes().byProjectOpenAll(destBranch.getParentKey())) {
abandonOneChange(c);
}
db.close();
@@ -1136,9 +1230,8 @@
err = e;
}
if (err != null) {
- log.warn(String.format(
- "Cannot abandon changes for deleted project %s",
- destBranch.getParentKey().get()), err);
+ logWarn("Cannot abandon changes for deleted project "
+ + destBranch.getParentKey().get(), err);
}
}
@@ -1184,4 +1277,34 @@
}
update.commit();
}
+
+ private void logDebug(String msg, Object... args) {
+ if (log.isDebugEnabled()) {
+ log.debug(logPrefix + msg, args);
+ }
+ }
+
+ private void logWarn(String msg, Throwable t) {
+ if (log.isWarnEnabled()) {
+ log.warn(logPrefix + msg, t);
+ }
+ }
+
+ private void logWarn(String msg) {
+ if (log.isWarnEnabled()) {
+ log.warn(logPrefix + msg);
+ }
+ }
+
+ private void logError(String msg, Throwable t) {
+ if (log.isErrorEnabled()) {
+ log.error(logPrefix + msg, t);
+ }
+ }
+
+ private void logError(String msg) {
+ if (log.isErrorEnabled()) {
+ log.error(logPrefix + msg);
+ }
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchesCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchesCollection.java
index 3bf3522..e96d3a5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchesCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchesCollection.java
@@ -16,12 +16,14 @@
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AcceptsCreate;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.server.project.ListBranches.BranchInfo;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.eclipse.jgit.lib.Constants;
@@ -34,12 +36,12 @@
ChildCollection<ProjectResource, BranchResource>,
AcceptsCreate<ProjectResource> {
private final DynamicMap<RestView<BranchResource>> views;
- private final ListBranches list;
+ private final Provider<ListBranches> list;
private final CreateBranch.Factory createBranchFactory;
@Inject
BranchesCollection(DynamicMap<RestView<BranchResource>> views,
- ListBranches list, CreateBranch.Factory createBranchFactory) {
+ Provider<ListBranches> list, CreateBranch.Factory createBranchFactory) {
this.views = views;
this.list = list;
this.createBranchFactory = createBranchFactory;
@@ -47,18 +49,18 @@
@Override
public RestView<ProjectResource> list() {
- return list;
+ return list.get();
}
@Override
public BranchResource parse(ProjectResource parent, IdString id)
- throws ResourceNotFoundException, IOException {
+ throws ResourceNotFoundException, IOException, BadRequestException {
String branchName = id.get();
if (!branchName.startsWith(Constants.R_REFS)
&& !branchName.equals(Constants.HEAD)) {
branchName = Constants.R_HEADS + branchName;
}
- List<BranchInfo> branches = list.apply(parent);
+ List<BranchInfo> branches = list.get().apply(parent);
for (BranchInfo b : branches) {
if (branchName.equals(b.ref)) {
return new BranchResource(parent.getControl(), b);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index 03b13c7..13ad817 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -66,20 +66,6 @@
}
}
- public ChangeControl controlFor(Change.Id id, CurrentUser user)
- throws NoSuchChangeException {
- final Change change;
- try {
- change = db.get().changes().get(id);
- if (change == null) {
- throw new NoSuchChangeException(id);
- }
- } catch (OrmException e) {
- throw new NoSuchChangeException(id, e);
- }
- return controlFor(change, user);
- }
-
public ChangeControl validateFor(Change change, CurrentUser user)
throws NoSuchChangeException, OrmException {
ChangeControl c = controlFor(change, user);
@@ -88,15 +74,6 @@
}
return c;
}
-
- public ChangeControl validateFor(Change.Id id, CurrentUser user)
- throws NoSuchChangeException, OrmException {
- ChangeControl c = controlFor(id, user);
- if (!c.isVisible(db.get())) {
- throw new NoSuchChangeException(c.getChange().getId());
- }
- return c;
- }
}
public static class Factory {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java
index 01e705a..6e07fb4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java
@@ -14,12 +14,15 @@
package com.google.gerrit.server.project;
+import com.google.common.base.Predicate;
import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gerrit.extensions.common.ActionInfo;
import com.google.gerrit.extensions.common.WebLinkInfo;
import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.RestView;
@@ -29,29 +32,44 @@
import com.google.gerrit.server.extensions.webui.UiActions;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
-import com.google.inject.Singleton;
import com.google.inject.util.Providers;
+import dk.brics.automaton.RegExp;
+import dk.brics.automaton.RunAutomaton;
+
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
+import org.kohsuke.args4j.Option;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
-@Singleton
public class ListBranches implements RestReadView<ProjectResource> {
private final GitRepositoryManager repoManager;
private final DynamicMap<RestView<BranchResource>> branchViews;
private final WebLinks webLinks;
+ @Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "maximum number of branches to list")
+ private int limit;
+
+ @Option(name = "--start", aliases = {"-s"}, metaVar = "CNT", usage = "number of branches to skip")
+ private int start;
+
+ @Option(name = "--match", aliases = {"-m"}, metaVar = "MATCH", usage = "match branches substring")
+ private String matchSubstring;
+
+ @Option(name = "--regex", aliases = {"-r"}, metaVar = "REGEX", usage = "match branches regex")
+ private String matchRegex;
+
@Inject
public ListBranches(GitRepositoryManager repoManager,
DynamicMap<RestView<BranchResource>> branchViews,
@@ -63,7 +81,7 @@
@Override
public List<BranchInfo> apply(ProjectResource rsrc)
- throws ResourceNotFoundException, IOException {
+ throws ResourceNotFoundException, IOException, BadRequestException {
List<BranchInfo> branches = Lists.newArrayList();
BranchInfo headBranch = null;
@@ -150,6 +168,61 @@
if (headBranch != null) {
branches.add(0, headBranch);
}
+
+ List<BranchInfo> filteredBranches;
+ if ((matchSubstring != null && !matchSubstring.isEmpty())
+ || (matchRegex != null && !matchRegex.isEmpty())) {
+ filteredBranches = filterBranches(branches);
+ } else {
+ filteredBranches = branches;
+ }
+ if (!filteredBranches.isEmpty()) {
+ int end = filteredBranches.size();
+ if (limit > 0 && start + limit < end) {
+ end = start + limit;
+ }
+ if (start <= end) {
+ filteredBranches = filteredBranches.subList(start, end);
+ } else {
+ filteredBranches = Collections.emptyList();
+ }
+ }
+ return filteredBranches;
+ }
+
+ private List<BranchInfo> filterBranches(List<BranchInfo> branches)
+ throws BadRequestException {
+ if (matchSubstring != null) {
+ return ((List<BranchInfo>) Lists.newArrayList(Iterables.filter(branches,
+ new Predicate<BranchInfo>() {
+ public boolean apply(BranchInfo in) {
+ return in.ref.toLowerCase(Locale.US).contains(
+ matchSubstring.toLowerCase(Locale.US));
+ }
+ })));
+ } else if (matchRegex != null) {
+ if (matchRegex.startsWith("^")) {
+ matchRegex = matchRegex.substring(1);
+ if (matchRegex.endsWith("$") && !matchRegex.endsWith("\\$")) {
+ matchRegex = matchRegex.substring(0, matchRegex.length() - 1);
+ }
+ }
+ if (matchRegex.equals(".*")) {
+ return branches;
+ }
+ try {
+ final RunAutomaton a =
+ new RunAutomaton(new RegExp(matchRegex).toAutomaton());
+ return ((List<BranchInfo>) Lists.newArrayList(Iterables.filter(
+ branches, new Predicate<BranchInfo>() {
+ public boolean apply(BranchInfo in) {
+ return a.run(in.ref);
+ }
+ })));
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestException(e.getMessage());
+ }
+ }
return branches;
}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/config/CapabilityConstants.properties b/gerrit-server/src/main/resources/com/google/gerrit/server/config/CapabilityConstants.properties
index 056da87..c8e95bc 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/config/CapabilityConstants.properties
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/config/CapabilityConstants.properties
@@ -5,7 +5,6 @@
createProject = Create Project
emailReviewers = Email Reviewers
flushCaches = Flush Caches
-generateHttpPassword = Generate HTTP Password
killTask = Kill Task
modifyAccount = Modify Account
priority = Priority
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
index 61f5fa40..39a9fe7 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
@@ -269,9 +269,8 @@
private String extractWhat(DispatchCommand dcmd) {
String commandName = dcmd.getCommandName();
- String[] args = dcmd.getArguments();
- if (args.length > 1) {
- return commandName + "." + args[1];
+ for (String arg : dcmd.getArguments()) {
+ commandName = commandName + "." + arg;
}
return commandName;
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index 76a5f24..3b3df96 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -18,6 +18,7 @@
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.errors.EmailException;
import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RawInput;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -260,7 +261,7 @@
}
}
- private void deleteSshKey(SshKeyInfo i) throws OrmException {
+ private void deleteSshKey(SshKeyInfo i) throws AuthException, OrmException {
AccountSshKey sshKey = new AccountSshKey(
new AccountSshKey.Id(user.getAccountId(), i.seq), i.sshPublicKey);
deleteSshKey.apply(
diff --git a/lib/codemirror/BUCK b/lib/codemirror/BUCK
index e8539c6..a18c761 100644
--- a/lib/codemirror/BUCK
+++ b/lib/codemirror/BUCK
@@ -2,8 +2,8 @@
include_defs('//lib/codemirror/cm3.defs')
include_defs('//lib/codemirror/closure.defs')
-VERSION = '28a638a984'
-SHA1 = '68f8f136092a5965778186fb401a33be34cf73ed'
+VERSION = '57e7ed7177'
+SHA1 = 'd78bc5518707960647d0b8b85d9f1ac011b785d5'
URL = GERRIT + 'net/codemirror/codemirror-%s.zip' % VERSION
ZIP = 'codemirror-%s.zip' % VERSION
diff --git a/plugins/cookbook-plugin b/plugins/cookbook-plugin
index a2e56f0..15efdb1 160000
--- a/plugins/cookbook-plugin
+++ b/plugins/cookbook-plugin
@@ -1 +1 @@
-Subproject commit a2e56f0f76dac45a5084c28a27e24ba039b57e09
+Subproject commit 15efdb1f7e6e5f6e4db2791b8753cd57e6d5c790
diff --git a/plugins/replication b/plugins/replication
index 211e928..ada673d 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 211e9289cc5771293855845d31bc0ce756545851
+Subproject commit ada673decd7b603734dbbadb31510219fa5b1123
diff --git a/tools/java_doc.defs b/tools/java_doc.defs
index d117bda..514a730 100644
--- a/tools/java_doc.defs
+++ b/tools/java_doc.defs
@@ -1,7 +1,7 @@
def java_doc(
name,
title,
- pkg,
+ pkgs,
paths,
srcs = [],
deps = [],
@@ -24,7 +24,8 @@
'-notimestamp',
'-windowtitle "' + title + '"',
'-link http://docs.oracle.com/javase/7/docs/api',
- '-subpackages ' + pkg,
+ '-subpackages ',
+ ':'.join(pkgs),
'-sourcepath ',
':'.join(sourcepath),
' -classpath ',