Merge "Create and use a StarCache in the ChangeScreen"
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index e7379ea..83c0789 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -712,6 +712,7 @@
`Code Review`, above) must enable submit, and also must not block it.
See above for details on each category.
+
[[category_makeoneup]]
Your Category Here
~~~~~~~~~~~~~~~~~~
@@ -780,6 +781,30 @@
clear` is required to enable submit.
+Examples of typical roles in a project
+--------------------------------------
+
+Below follows a set of typical roles on a server and which access
+rights these roles typically should be granted. You may see them as
+general guide lines for a typical way to set up your project on a
+brand new Gerrit instance.
+
+[[examples_contributor]]
+Contributor
+~~~~~~~~~~~
+
+This is the typical user on a public server. They are able to read
+your project and upload new changes to it. They are able to give
+feedback on other changes as well, but are unable to block or approve
+any changes.
+
+Suggested access rights to grant:
+
+* <<category_read,`Read`>> on 'refs/heads/\*' and 'refs/tags/*'
+* <<category_push,`Push`>> to 'refs/for/refs/heads/\*' and 'refs/changes/*'
+* <<category_label-Code-Review,`Code review`>> with range '-1' to '+1'
+
+
[[conversion_table]]
Conversion table from 2.1.x series to 2.2.x series
--------------------------------------------------
@@ -833,16 +858,6 @@
Below you find a list of capabilities available:
-* Create Group
-
-* Create Project
-
-* Flush Caches
-
-* Kill Task
-
-* Priority
-
* Start Replication
* View Caches
@@ -866,13 +881,82 @@
Create Account
~~~~~~~~~~~~~~
-Allow link:cmd-create-account.html['account creation over the ssh prompt'].
+Allow link:cmd-create-account.html[account creation over the ssh prompt].
This capability allows the granted group members to create non-interactive
service accounts. These service accounts are generally used for automation
and made to be members of the
link:access-control.html#non-interactive_users['Non-Interactive users'] group.
+[[capability_createGroup]]
+Create Group
+~~~~~~~~~~~~
+
+Allow group creation. Groups are used to grant users access to different
+actions in projects. This capability allows the granted group members to
+either link:cmd-create-group.html[create new groups via ssh] or via the web UI.
+
+
+[[capability_createProject]]
+Create Project
+~~~~~~~~~~~~~~
+
+Allow project creation. This capability allows the granted group to
+either link:cmd-create-project.html[create new git projects via ssh]
+or via the web UI.
+
+
+[[capability_flushCaches]]
+Flush Caches
+~~~~~~~~~~~~
+
+Allow the flushing of Gerrits caches. This capability allows the granted
+group to link:cmd-flush-caches.html[flush some or all Gerrit caches via ssh].
+
+[NOTE]
+This capability doesn't imply permissions to the show-caches command. For that
+you need the <<capability_viewCaches,view caches capability>>.
+
+
+[[capability_kill]]
+Kill Task
+~~~~~~~~~
+
+Allow the operation of the link:cmd-kill.html[kill command over ssh]. The
+kill command ends tasks that currently occupy the Gerrit server, usually
+a replication task or a user initiated task such as an upload-pack or
+recieve-pack.
+
+
+[[capability_priority]]
+Priority
+~~~~~~~~
+
+This capability allows users to use
+link:config-gerrit.html#sshd.batchThreads[the thread pool reserved] for
+link:access-control.html#non-interactive_users['Non-Interactive Users'].
+It's a binary value in that granted users either have access to the thread
+pool, or they don't.
+
+There are three modes for this capability and they're listed by rising
+priority:
+
+No capability configured.::
+The user isn't a member of a group with any priority capability granted. By
+default the user is then in the 'INTERACTIVE' thread pool.
+
+'BATCH'::
+If there's a thread pool configured for 'Non-Interactive Users' and a user is
+granted the priority capability with the 'BATCH' mode selected, the user ends
+up in the separate batch user thread pool. This is true unless the user is
+also granted the below 'INTERACTIVE' option.
+
+'INTERACTIVE'::
+If a user is granted the priority capability with the 'INTERACTIVE' option,
+regardless if they also have the 'BATCH' option or not, they are in the
+'INTERACTIVE' thread pool.
+
+
[[capability_queryLimit]]
Query Limit
~~~~~~~~~~~
diff --git a/Documentation/cmd-create-account.txt b/Documentation/cmd-create-account.txt
index 31bc482..98f950f 100644
--- a/Documentation/cmd-create-account.txt
+++ b/Documentation/cmd-create-account.txt
@@ -28,7 +28,8 @@
ACCESS
------
Caller must be a member of the privileged 'Administrators' group,
-or have been granted the 'Create Account' global capability.
+or have been granted
+link:access-control.html#capability_createAccount[the 'Create Account' global capability].
SCRIPTING
---------
diff --git a/Documentation/cmd-create-group.txt b/Documentation/cmd-create-group.txt
index 7549c38..475d2c5 100644
--- a/Documentation/cmd-create-group.txt
+++ b/Documentation/cmd-create-group.txt
@@ -28,7 +28,8 @@
ACCESS
------
Caller must be a member of the privileged 'Administrators' group,
-or have been granted the 'Create Group' global capability.
+or have been granted
+link:access-control.html#capability_createGroup[the 'Create Group' global capability].
SCRIPTING
---------
diff --git a/Documentation/cmd-create-project.txt b/Documentation/cmd-create-project.txt
index cc7e929..f22141c 100644
--- a/Documentation/cmd-create-project.txt
+++ b/Documentation/cmd-create-project.txt
@@ -39,7 +39,8 @@
ACCESS
------
Caller must be a member of the privileged 'Administrators' group,
-or have been granted the 'Create Project' global capability.
+or have been granted
+link:access-control.html#capability_createProject[the 'Create Project' global capability].
SCRIPTING
---------
diff --git a/Documentation/cmd-flush-caches.txt b/Documentation/cmd-flush-caches.txt
index f8882c8..bc6fac5 100644
--- a/Documentation/cmd-flush-caches.txt
+++ b/Documentation/cmd-flush-caches.txt
@@ -26,7 +26,8 @@
ACCESS
------
Caller must be a member of the privileged 'Administrators' group,
-or have granted the 'Flush Caches' global capability.
+or in a group that have been granted
+link:access-control.html#capability_flushCaches[the 'Flush Caches' global capability].
SCRIPTING
---------
diff --git a/Documentation/cmd-kill.txt b/Documentation/cmd-kill.txt
index 57a98f7..f09053e 100644
--- a/Documentation/cmd-kill.txt
+++ b/Documentation/cmd-kill.txt
@@ -19,7 +19,7 @@
ACCESS
------
Caller must be a member of the privileged 'Administrators' group,
-or have been granted the 'Kill Task' global capability.
+or have been granted link:access-control.html#capability_kill[the 'Kill Task' global capability].
SCRIPTING
---------
diff --git a/Documentation/cmd-query.txt b/Documentation/cmd-query.txt
index 1bd4862..253bed1 100644
--- a/Documentation/cmd-query.txt
+++ b/Documentation/cmd-query.txt
@@ -15,6 +15,7 @@
[--files]
[--comments]
[--commit-message]
+ [--dependencies]
[--]
<query>
[limit:<n>]
@@ -75,6 +76,10 @@
--commit-message::
Include the full commit message in the change description.
+--dependencies::
+ Show information about patch sets which depend on, or are needed by,
+ each patch set.
+
limit:<n>::
Maximum number of results to return. This is actually a
query operator, and not a command line option. If more
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index ec55b57..d02382f 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1957,8 +1957,9 @@
[[sshd.batchThreads]]sshd.batchThreads::
+
Number of threads to allocate for SSH command requests from
-non-interactive users. If equals to 0, then all non-interactive
-requests are executed in the same queue as interactive requests.
+link:access-control.html#non-interactive_users[non-interactive users].
+If equals to 0, then all non-interactive requests are executed in the same
+queue as interactive requests.
+
Any other value will remove the number of threads from the queue
allocated to interactive users, and create a separate thread pool
diff --git a/ReleaseNotes/ReleaseNotes-2.3.txt b/ReleaseNotes/ReleaseNotes-2.3.txt
new file mode 100644
index 0000000..20b14c6
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.3.txt
@@ -0,0 +1,454 @@
+Release notes for Gerrit 2.3
+============================
+
+Gerrit 2.3 is now available:
+
+link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.3.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.3.war]
+
+Schema Change
+-------------
+*WARNING:* This release contains schema changes. To upgrade:
+----
+ java -jar gerrit.war init -d site_path
+----
+
+*WARNING:* Upgrading to 2.3.x requires the server be first upgraded
+to 2.1.7 (or a later 2.1.x version), and then to 2.3.x.
+
+If you are upgrading from 2.2.x.x, you may ignore this warning and
+upgrade directly to 2.3.x.
+
+
+New Features
+------------
+Drafts
+~~~~~~
+* New draft statuses and magic branches
++
+Adds draft status to Change. DRAFT status in change occurs before NEW
+and will be for a change that is not meant for review (yet).
+Also adds magic branches refs/drafts/ and refs/publish/ that
+will handle whether or not a patchset is a draft or goes straight to
+review. refs/for/ should be deprecated in favor of explicitly marking
+a patchset as a draft or directly to review.
+
+* Draft patchset and change visibility in UI
++
+If a patchset is a draft, adds a (DRAFT) label next to the revision
+(or gitweb link if it exists). If a change is a draft, adds a (DRAFT)
+next to the subject and changes the status appropriately.
+
+* Publish draft patchsets in UI and SSH
++
+Adds Publish button to draft patchsets in UI and an option to
+publish draft patchsets in the review ssh command. Publishing a draft
+patchset makes it visible. Publishing a draft patchset in a draft
+change irreversibly upgrades the change status to NEW.
+
+* Delete draft changes and patchsets
++
+Adds ability to delete draft changes and patchsets that are not meant
+or fit for code review. Deleting a draft patchset also deletes the
+corresponding ref from the repository and decrements the next patch
+set number for the change if necessary. Deleting a draft change
+deletes all of its (draft) patchsets.
+
+* Add pushing drafts to refs/drafts/
++
+Pushing to refs/drafts/ will now push a draft patchset. If this is the
+first patch set, change created will be in draft status. Pushing a
+draft patchset to a draft change keeps it in draft status. Pushing
+a non-draft patchset (with refs/publish/ or refs/for/, they do the
+same thing) to a draft change turns it into a non-draft change.
+Draft patchsets cannot be submitted.
+
+* When pushing changes as drafts, output [DRAFT] next to the change link
+
+
+Web
+~~~
+* issue 203 Create project through web interface
++
+Add a new panel in the Admin->Projects Screen. It
+enables the users that are allowed to create projects
+via command-line to create them also via web interface.
+
+* Suggest parent for 'create-project' in the UI.
++
+Add a list of parent suggestions for 'create project'
+in the UI, so the user can select a parent for the new
+project from a list of projects that are already parents to
+other projects.
+
+* issue 981 Fix diffs skipping one line
++
+Don't show '... skipping 1 common line ...'. The text to show this
+takes up just as much space as showing the line which was skipped.
+
+* issue 18 Support expanding lines of context in diff
++
+Allow lines of context which were skipped in the side-by-side diff
+view to be expanded. This makes it easier to get more code context
+when needed but not show huge amounts of unneeded data.
+
+* Move checkbox to mark file as reviewed into title bar
+
+* Redirect the user to the reverted change (when reverting).
+
+* On group rename update the group name in the page title
+
+* In ProjectAccessScreen add link to history of project.config in gitweb
+
+* Removed superfluous 'comment' for patch history table.
+
+* Make OpenID login images transparent
+
+* Disable SSH Keys in the web UI if SSHD is disabled
+
+
+SSH
+~~~
+* Adds --description (-d) option to ls-projects
++
+Allows listing of projects together with their respective
+description.
+
+* ls-projects: new option to list all accessible projects
++
+Add a new option '--all' to the 'ls-projects' SSH command to display
+all projects that are accessible by the calling user account. Besides
+the projects that the calling user account has been granted 'READ'
+access to, this includes all projects that are owned by the calling
+user account (even if for these projects the 'READ' access right is
+not assigned to the calling user account).
+
+* Suggest parent for 'create-project' in the SSH command
++
+Add an option '--suggest-parents' which will print out
+a list of projects that are already parents to another
+projects, thus it can help user to find a suitable
+parent for the new project.
+
+* Support reparenting all children of a parent project
++
+This change adds a new option to the 'set-project-parent' command that
+allows reparenting all child projects of one parent project to another
+parent project.
+
+* set-parent-project: evict child projects from project cache
+
+* Add ssh command to list groups.
+
+* ls-groups: add option to list groups for a project
++
+Add an option to the ls-groups SSH command that allows to list only
+those groups for which any permission is assigned to a project.
+
+* ls-groups: Add option to only list groups that are visible to all
+
+* ls-groups: Support listing groups by group type
+
+* ls-groups: Support listing of groups for a user
+
+* Add new SSH command to rename groups
+
+* Support for --file option for ssh queries.
++
+Allows user to list files and attributes (ADDED,
+MODIFIED, DELETED, RENAMED, COPIED) when querying for
+patch sets.
+
+* Output full commit message in query results
+
+* Option for SSHD review-cmd to always publish the message.
++
+"--force-message" option for the SSHD review command,
+which allows Gerrit to publish the "--message", even if the
+labels could not be applied due to change being closed.
+
+
+Config
+~~~~~~
+* issue 349 Apply states for projects (active, readonly and hidden)
++
+Active state indicates the project is regular and is the default value.
++
+Read Only means that users can see the project if read permission is
+granted, but all modification operations are disabled.
++
+Hidden means the project is not visible for those who are not owners
+
+* Enable case insensitive login to Gerrit WebUI for LDAP authentication
++
+Gerrit treats user names as case sensitive, while some LDAP servers
+don't. On first login to Gerrit the user enters his user name and
+Gerrit queries LDAP for it. Since LDAP is case-insensitive with regards
+to the username, the LDAP authentication succeeds regardless in
+which case the user typed in his user name. The username is stored in
+Gerrit exactly as entered by the user. For further logins the user
+always has to use the same case. If the user specifies his user name in
+a different case Gerrit tries to create a new account which fails with
+"Cannot assign user name ... to account ...; name already in use.".
+This error occurs because the LDAP query resolves to the same LDAP
+user and storing the username for SSH (which is by default always
+lower case) fails because such an entry exists already for the first
+account that the user created.
++
+This change introduces a new configuration parameter that converts the
+user name always to lower case before doing the LDAP authentication.
+By this the login to the Gerrit WebUI gets case insensitive. If this
+configuration parameter is set, the user names for all existing
+accounts have to be converted to lower case. This change includes a
+server program to do this conversion.
+
+* Enable case insensitive authentication for git operations
++
+A new configuration parameter is introduced that converts the username
+that is received to authenticate a git operation to lower case for
+looking up the user account in Gerrit.
++
+By setting this parameter a case insensitive authentication for the
+git operations can be achieved, if it is ensured that the usernames in
+Gerrit (scheme 'username') are stored in lower case (e.g. if the
+parameter 'ldap.accountSshUserName' is set to
+'${sAMAccountName.toLowerCase}').
+
+* Support replication to local folder
+
+* Read timeout parameter for LDAP connections: ldap.readTimeout
++
+This helps prevent a very slow LDAP server from blocking
+all SSH command creation threads.
+
+* Introduce a git maxObjectSizeLimit in the [recieve] config
++
+This limits the size of uploaded files
+
+* Make 'Anonymous Coward' configurable
+
+* Add property to configure path separator in URLs for a gitweb service
+
+* Customize link-name pointing to gitweb-service.
++
+Previously the link to the external gitweb-type
+pages said "(gitweb)" regardless if using cgit
+or a custom service.
+
+* Support gitweb.type=disabled
+
+* rules.enable: Support disabling per project prolog rules in gerrit.config
+
+* Allow site administrators to define Git-over-HTTP mirror URL
+
+* Allow sshd.listenAddress = off to disable the daemon
+
+* daemon: Allow httpd without sshd
+
+* Allow disabling certain features of HostPageServlet
++
+These features are: user agent detection and automatic refresh
+logic associated with the site header, footer and CSS.
+
+
+Dev
+~~~
+* Fix 'No source code is available for type org.eclipse.jgit.lib.Constants'
+
+* Fix miscellaneous compiler warnings
+
+* Add entries to .gitignore for m2e settings/preference files
+
+* Package source JARs for antlr, httpd, server
+
+* pom.xml: change gerrit-war's dependency on gerrit-main to runtime
++
+This only seems to matter to IntelliJ, since the Main class is
+provided to the war via an overlay in gerrit-war/pom.xml
+
+* Fixed the full name of the MAVEN2_CLASSPATH_CONTAINER
++
+Fixes java.lang.NoClassDefFoundError: com/google/gwt/dev/DevMode
+
+
+Miscellaneous
+~~~~~~~~~~~~~
+* Allow superprojects to subscribe to submodules updates
++
+The feature introduced in this commit allows superprojects to
+subscribe to submodules updates.
++
+When a commit is merged to a project, the commit content is scanned
+to identify if it registers submodules (if the commit contains new
+gitlinks and .gitmodules file with required info) and if so, a new
+submodule subscription is registered.
++
+When a new commit of a registered submodule is merged, gerrit
+automatically updates the subscribers to the submodule with new
+commit having the updated gitlinks.
++
+The most notable benefit of the feature is to not require
+to push/merge commits of super projects (subscribers) with gitlinks
+whenever a project being a submodule is updated. It is only
+required to push commits with gitlinks when they are created
+(and in this case it is also required to push .gitmodules file).
+
+* Allow Realm to participate when linking an account identity
++
+When linking a new user identity to an exisiting account, permit the
+Realm to observe the new incoming identity and the current account,
+and to alter the request. This enables a Realm to observe when a
+user verifies a new email address link.
+
+* issue 871 Show latest patchset with cherry-picked merge
++
+When a change is published via the cherry-pick merge strategy,
+show the final commit as a patchset in the change history.
+This now makes it possible to search for the cherry-picked SHA1.
+
+* issue 871 Display hash of the cherry-pick merge in comment
+
+* Added more verbose messages when changes are being rejected
+
+* Display proper error message when LDAP is unavailable
+
+* Clarify error msg when user's not allowed to '--force push'.
+
+* ContainerAuthFilter: fail with FORBIDDEN if username not set
+
+* Resolve 'Project Owners' group if it is included into another group
+
+* Hide SSH URL in email footers if SSH is disabled
+
+* Sort the jar files from the war before adding to classpath.
+
+* Apply user preferences when loading site
+
+* Ensure HttpLog can always get the user identity
+
+* Prevent comments spam for abandoned commit
++
+If some change was abandoned but later submitted (e.g. by
+cherry-picking it to a another branch) then pushing a new branch
+that contains this change no longer adds a new comment.
+
+* Make Address, EmailHeader visible to other EmailSenders
+
+* Use transactions to handle comments when possible
+
+* Try to use transactions when creating changes
+
+* gerrit.sh: disown doesn't accept pid as a argument, fix script
+
+* gerrit.sh: detach gerrit properly so it won't keep bad ssh sessions open.
+
+* Cache list of all groups in the group cache
+
+* issue 1161 Evict project in user cache on save of project meta data
+
+* Ensure that the site paths are resolved to their canonical form (for Windows)
+
+* Connect Velocity to slf4j
+
+* Expose project permissionOnly status via JSON-RPC
+
+* Make HEAD of All-Projects point to refs/meta/config
+
+* issue 1158 Added support for European style dates
+
+* Make macros in email templates local to the template
+
+* Support http://server/project for Git access
+
+* Use _ instead of $ for implementation-detail Prolog predicates
+
+* Update the Sign In anchor with current URL
++
+Always update the href of the Sign In anchor in the menu bar with
+the current page URL after /login/, making the redirect process
+bring users back to the current view after sign in.
+
+* Improve validation of email registration tokens
+
+
+Upgrades
+--------
+* Upgrade to gwtorm 1.2
+
+* Upgrade to JGit 1.1.0.201109151100-r.119-gb4495d1
++
+This is needed because of this change:
+https://gerrit-review.googlesource.com/#/c/30450/
+
+* Support Velocity 1.5 (as well as previous 1.6.4)
+
+
+Bug Fixes
+---------
+* Avoid NPE when group is missing
+
+* Do not fail with NPE if context path of request is null
+
+* Fix NPE in set-project-parent command if parent is not specified
+
+* Only send mail to author and committer if they are registered to prevent an NPE
+
+* Avoid potential NPE when querying the queue.
+
+* Allow loading Project Access when there is no refs/meta/config
+
+* Fix calculation of project name if repo is not existing
++
+If a project inherits from a non existing parent, prevent a
+StringIndexOutOfBoundsException.
+
+* Fix: Supress "Error on refs/cache-automerge" warnings.
+
+* Don't allow registering for cleanup after cleanup runs
++
+This prevents leaking a database connection.
+
+* issue 807 Fix: Tags are not replicated properly
+
+* Prevent smtp rejected users from rejecting emails for all users
+
+* Fix token saving redirect in container auth
++
+Update the jump page that redirects users from /#TOKEN to
+/login/TOKEN. This forces using the container based
+authentication. Also correct "/login//" to be just "/login/".
+
+* Use custom error messages for Git-over-HTTP
++
+Ensure clients see messages related to contributor agreement not
+being activated even if they push over HTTP.
+
+* Avoid double key event for GroupReferenceBox
+
+* Fix git push authentication over HTTP
+
+* Fix http://login/ redirect bug
+
+* Fix missing targets in /login/ URLs
+
+* set-project-parent: if update of 1 project fails continue with others
+
+* Verify the case of the project name before opening git repository
+
+* Update top level SUBMITTING_PATCHES URLs
+
+
+Documentation
+-------------
+* Some updates to the design docs
+
+* cmd-index: Fix link to documentation of rename-group command
+
+* Update documentation for testing SSH connection
+
+* Bypass review updated with 2.2.x permissions
+
+* Add documentation for 'peer_keys'
+
+* Improve 'Push Merge Commit' access right documentation
+
+* Access control: capabilities
diff --git a/ReleaseNotes/index.txt b/ReleaseNotes/index.txt
index 8d223ad..13da60a 100644
--- a/ReleaseNotes/index.txt
+++ b/ReleaseNotes/index.txt
@@ -1,6 +1,11 @@
Gerrit Code Review - Release Notes
==================================
+[[2_3]]
+Version 2.3.x
+-------------
+* link:ReleaseNotes-2.3.html[2.3]
+
[[2_2]]
Version 2.2.x
-------------
diff --git a/gerrit-ehcache/.gitignore b/gerrit-ehcache/.gitignore
index 903c6c8..20251d4 100644
--- a/gerrit-ehcache/.gitignore
+++ b/gerrit-ehcache/.gitignore
@@ -1,4 +1,5 @@
/target
/.classpath
/.project
+/.settings/org.eclipse.m2e.core.prefs
/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeAttribute.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeAttribute.java
index bb5abe4..9810f59 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeAttribute.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeAttribute.java
@@ -39,4 +39,7 @@
public List<TrackingIdAttribute> trackingIds;
public PatchSetAttribute currentPatchSet;
public List<PatchSetAttribute> patchSets;
+
+ public List<DependencyAttribute> dependsOn;
+ public List<DependencyAttribute> neededBy;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/DependencyAttribute.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/DependencyAttribute.java
new file mode 100644
index 0000000..47fbdac
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/DependencyAttribute.java
@@ -0,0 +1,23 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.events;
+
+public class DependencyAttribute {
+ public String id;
+ public String number;
+ public String revision;
+ public String ref;
+ public Boolean isCurrentPatchSet;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
index bc9047d..4d34b71 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -22,13 +22,18 @@
import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.PatchSetAncestor;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.client.TrackingId;
+import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListEntry;
+import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -47,16 +52,18 @@
private final Provider<String> urlProvider;
private final ApprovalTypes approvalTypes;
private final PatchListCache patchListCache;
+ private final SchemaFactory<ReviewDb> schema;
@Inject
EventFactory(AccountCache accountCache,
@CanonicalWebUrl @Nullable Provider<String> urlProvider,
ApprovalTypes approvalTypes,
- PatchListCache patchListCache) {
+ PatchListCache patchListCache, SchemaFactory<ReviewDb> schema) {
this.accountCache = accountCache;
this.urlProvider = urlProvider;
this.approvalTypes = approvalTypes;
this.patchListCache = patchListCache;
+ this.schema = schema;
}
/**
@@ -110,6 +117,61 @@
a.status = change.getStatus();
}
+ public void addDependencies(ChangeAttribute ca, Change change) {
+ ca.dependsOn = new ArrayList<DependencyAttribute>();
+ ca.neededBy = new ArrayList<DependencyAttribute>();
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ final PatchSet.Id psId = change.currentPatchSetId();
+ for (PatchSetAncestor a : db.patchSetAncestors().ancestorsOf(psId)) {
+ for (PatchSet p :
+ db.patchSets().byRevision(a.getAncestorRevision())) {
+ Change c = db.changes().get(p.getId().getParentKey());
+ ca.dependsOn.add(newDependsOn(c, p));
+ }
+ }
+
+ final RevId revId = db.patchSets().get(psId).getRevision();
+ for (PatchSetAncestor a : db.patchSetAncestors().descendantsOf(revId)) {
+ final PatchSet p = db.patchSets().get(a.getPatchSet());
+ final Change c = db.changes().get(p.getId().getParentKey());
+ ca.neededBy.add(newNeededBy(c, p));
+ }
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ // Squash DB exceptions and leave dependency lists partially filled.
+ }
+ // Remove empty lists so a confusing label won't be displayed in the output.
+ if (ca.dependsOn.isEmpty()) {
+ ca.dependsOn = null;
+ }
+ if (ca.neededBy.isEmpty()) {
+ ca.neededBy = null;
+ }
+ }
+
+ private DependencyAttribute newDependsOn(Change c, PatchSet ps) {
+ DependencyAttribute d = newDependencyAttribute(c, ps);
+ d.isCurrentPatchSet = c.currPatchSetId().equals(ps.getId());
+ return d;
+ }
+
+ private DependencyAttribute newNeededBy(Change c, PatchSet ps) {
+ return newDependencyAttribute(c, ps);
+ }
+
+ private DependencyAttribute newDependencyAttribute(Change c, PatchSet ps) {
+ DependencyAttribute d = new DependencyAttribute();
+ d.number = c.getId().toString();
+ d.id = c.getKey().toString();
+ d.revision = ps.getRevision().get();
+ d.ref = ps.getRefName();
+ return d;
+ }
+
public void addTrackingIds(ChangeAttribute a, Collection<TrackingId> ids) {
if (!ids.isEmpty()) {
a.trackingIds = new ArrayList<TrackingIdAttribute>(ids.size());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/AsyncReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/AsyncReceiveCommits.java
index c2fe1a2..660b818 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/AsyncReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/AsyncReceiveCommits.java
@@ -17,7 +17,6 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.ReceiveCommits.MessageSender;
import com.google.gerrit.server.git.WorkQueue.Executor;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.util.RequestScopePropagator;
@@ -34,6 +33,7 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.PreReceiveHook;
import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.transport.ReceiveCommand.Result;
import org.eclipse.jgit.transport.ReceivePack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -112,26 +112,24 @@
}
private class MessageSenderOutputStream extends OutputStream {
- private final MessageSender messageSender = rc.getMessageSender();
-
@Override
public void write(int b) {
- messageSender.sendBytes(new byte[]{(byte)b});
+ rc.getMessageSender().sendBytes(new byte[]{(byte)b});
}
@Override
public void write(byte[] what, int off, int len) {
- messageSender.sendBytes(what, off, len);
+ rc.getMessageSender().sendBytes(what, off, len);
}
@Override
public void write(byte[] what) {
- messageSender.sendBytes(what);
+ rc.getMessageSender().sendBytes(what);
}
@Override
public void flush() {
- messageSender.flush();
+ rc.getMessageSender().flush();
}
}
@@ -167,14 +165,16 @@
timeoutMillis, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
log.warn("Error in ReceiveCommits", e);
- rc.getMessageSender().sendError("internal error while processing changes");
+ rc.addError("internal error while processing changes");
// ReceiveCommits has tried its best to catch errors, so anything at this
// point is very bad.
for (final ReceiveCommand c : commands) {
- if (c.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) {
- rc.reject(c, "internal error");
+ if (c.getResult() == Result.NOT_ATTEMPTED) {
+ c.setResult(Result.REJECTED_OTHER_REASON, "internal error");
}
}
+ } finally {
+ rc.sendMessages();
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MultiProgressMonitor.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MultiProgressMonitor.java
index 63edabea..dbb849c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MultiProgressMonitor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MultiProgressMonitor.java
@@ -253,7 +253,17 @@
private void sendDone() {
spinnerState = NO_SPINNER;
StringBuilder s = format();
- s.append(", done \n");
+ boolean any = false;
+ for (Task t : tasks) {
+ if (t.count != 0) {
+ any = true;
+ break;
+ }
+ }
+ if (any) {
+ s.append(",");
+ }
+ s.append(" done \n");
send(s);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 544673d..7be2b23 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -405,11 +405,11 @@
messages.add(new Message(message, false));
}
- private void addError(String error) {
+ void addError(String error) {
messages.add(new Message(error, true));
}
- private void sendMessages() {
+ void sendMessages() {
for (Message m : messages) {
if (m.isError) {
messageSender.sendError(m.message);
@@ -421,86 +421,82 @@
void processCommands(final Collection<ReceiveCommand> commands,
final MultiProgressMonitor progress) {
- try {
- newProgress = progress.beginSubTask("new", UNKNOWN);
- replaceProgress = progress.beginSubTask("updated", UNKNOWN);
- closeProgress = progress.beginSubTask("closed", UNKNOWN);
- commandProgress = progress.beginSubTask("refs", commands.size());
+ newProgress = progress.beginSubTask("new", UNKNOWN);
+ replaceProgress = progress.beginSubTask("updated", UNKNOWN);
+ closeProgress = progress.beginSubTask("closed", UNKNOWN);
+ commandProgress = progress.beginSubTask("refs", commands.size());
- parseCommands(commands);
- if (newChange != null
- && newChange.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) {
- createNewChanges();
+ parseCommands(commands);
+ if (newChange != null
+ && newChange.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) {
+ createNewChanges();
+ }
+ newProgress.end();
+
+ doReplaces();
+ replaceProgress.end();
+
+ for (final ReceiveCommand c : commands) {
+ if (c.getResult() == Result.OK) {
+ switch (c.getType()) {
+ case CREATE:
+ if (isHead(c)) {
+ autoCloseChanges(c);
+ }
+ break;
+
+ case UPDATE: // otherwise known as a fast-forward
+ tagCache.updateFastForward(project.getNameKey(),
+ c.getRefName(),
+ c.getOldId(),
+ c.getNewId());
+ if (isHead(c)) {
+ autoCloseChanges(c);
+ }
+ break;
+
+ case UPDATE_NONFASTFORWARD:
+ if (isHead(c)) {
+ autoCloseChanges(c);
+ }
+ break;
+ }
+
+ if (isConfig(c)) {
+ projectCache.evict(project);
+ ProjectState ps = projectCache.get(project.getNameKey());
+ repoManager.setProjectDescription(project.getNameKey(), //
+ ps.getProject().getDescription());
+ }
+
+ if (!MagicBranch.isMagicBranch(c.getRefName())) {
+ // We only schedule direct refs updates for replication.
+ // Change refs are scheduled when they are created.
+ //
+ replication.scheduleUpdate(project.getNameKey(), c.getRefName());
+ Branch.NameKey destBranch = new Branch.NameKey(project.getNameKey(), c.getRefName());
+ hooks.doRefUpdatedHook(destBranch, c.getOldId(), c.getNewId(), currentUser.getAccount());
+ }
+ commandProgress.update(1);
}
- newProgress.end();
+ }
+ closeProgress.end();
+ commandProgress.end();
+ progress.end();
- doReplaces();
- replaceProgress.end();
-
- for (final ReceiveCommand c : commands) {
- if (c.getResult() == Result.OK) {
- switch (c.getType()) {
- case CREATE:
- if (isHead(c)) {
- autoCloseChanges(c);
- }
- break;
-
- case UPDATE: // otherwise known as a fast-forward
- tagCache.updateFastForward(project.getNameKey(),
- c.getRefName(),
- c.getOldId(),
- c.getNewId());
- if (isHead(c)) {
- autoCloseChanges(c);
- }
- break;
-
- case UPDATE_NONFASTFORWARD:
- if (isHead(c)) {
- autoCloseChanges(c);
- }
- break;
- }
-
- if (isConfig(c)) {
- projectCache.evict(project);
- ProjectState ps = projectCache.get(project.getNameKey());
- repoManager.setProjectDescription(project.getNameKey(), //
- ps.getProject().getDescription());
- }
-
- if (!MagicBranch.isMagicBranch(c.getRefName())) {
- // We only schedule direct refs updates for replication.
- // Change refs are scheduled when they are created.
- //
- replication.scheduleUpdate(project.getNameKey(), c.getRefName());
- Branch.NameKey destBranch = new Branch.NameKey(project.getNameKey(), c.getRefName());
- hooks.doRefUpdatedHook(destBranch, c.getOldId(), c.getNewId(), currentUser.getAccount());
- }
- commandProgress.update(1);
+ if (!allNewChanges.isEmpty() && canonicalWebUrl != null) {
+ final String url = canonicalWebUrl;
+ addMessage("");
+ addMessage("New Changes:");
+ for (final Change c : allNewChanges) {
+ if (c.getStatus() == Change.Status.DRAFT) {
+ addMessage(" " + url + c.getChangeId() + " [DRAFT]");
+ }
+ else {
+ addMessage(" " + url + c.getChangeId());
}
}
- closeProgress.end();
- commandProgress.end();
- progress.end();
-
- if (!allNewChanges.isEmpty() && canonicalWebUrl != null) {
- final String url = canonicalWebUrl;
- addMessage("");
- addMessage("New Changes:");
- for (final Change c : allNewChanges) {
- if (c.getStatus() == Change.Status.DRAFT) {
- addMessage(" " + url + c.getChangeId() + " [DRAFT]");
- }
- else {
- addMessage(" " + url + c.getChangeId());
- }
- }
- addMessage("");
- }
- } finally {
- sendMessages();
+ addMessage("");
}
}
@@ -2060,7 +2056,7 @@
reject(cmd, "prohibited by Gerrit");
}
- void reject(final ReceiveCommand cmd, final String why) {
+ private void reject(final ReceiveCommand cmd, final String why) {
cmd.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, why);
commandProgress.update(1);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
index 92a6e30..619bc06 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
@@ -296,7 +296,7 @@
//
for (StarredChange w : args.db.get().starredChanges().byChange(
change.getId())) {
- add(RecipientType.BCC, w.getAccountId());
+ super.add(RecipientType.BCC, w.getAccountId());
}
} catch (OrmException err) {
// Just don't BCC everyone. Better to send a partial message to those
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java
index 7a75996..9705b8a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java
@@ -67,6 +67,7 @@
add(RecipientType.TO, reviewers);
add(RecipientType.CC, extraCC);
rcptToAuthors(RecipientType.CC);
+ bccStarredBy();
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
index e6848ed..a2fa7fe 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
@@ -77,6 +77,7 @@
private boolean includeComments;
private boolean includeFiles;
private boolean includeCommitMessage;
+ private boolean includeDependencies;
private OutputStream outputStream = DisabledOutputStream.INSTANCE;
private PrintWriter out;
@@ -128,6 +129,14 @@
return includeFiles;
}
+ public void setIncludeDependencies(boolean on) {
+ includeDependencies = on;
+ }
+
+ public boolean getIncludeDependencies() {
+ return includeDependencies;
+ }
+
public void setIncludeCommitMessage(boolean on) {
includeCommitMessage = on;
}
@@ -242,6 +251,10 @@
}
}
+ if (includeDependencies) {
+ eventFactory.addDependencies(c, d.getChange());
+ }
+
show(c);
}
@@ -367,7 +380,20 @@
out.print('\n');
} else if (value instanceof Collection) {
out.print('\n');
+ boolean firstElement = true;
for (Object thing : ((Collection<?>) value)) {
+ // The name of the collection was initially printed at the beginning
+ // of this routine. Beginning at the second sub-element, reprint
+ // the collection name so humans can separate individual elements
+ // with less strain and error.
+ //
+ if (firstElement) {
+ firstElement = false;
+ } else {
+ out.print(indent);
+ out.print(field);
+ out.print(":\n");
+ }
if (isPrimitive(thing)) {
out.print(' ');
out.print(value);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/GuiceRequestScopePropagator.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/GuiceRequestScopePropagator.java
index d5e047b..9befc7d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/util/GuiceRequestScopePropagator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/GuiceRequestScopePropagator.java
@@ -36,9 +36,9 @@
/** Propagator for Guice's built-in servlet scope. */
public class GuiceRequestScopePropagator extends RequestScopePropagator {
- private final Provider<String> urlProvider;
- private final Provider<SocketAddress> remotePeerProvider;
- private final Provider<CurrentUser> currentUserProvider;
+ private final String url;
+ private final SocketAddress peer;
+ private final CurrentUser user;
@Inject
GuiceRequestScopePropagator(
@@ -46,9 +46,9 @@
@RemotePeer Provider<SocketAddress> remotePeerProvider,
Provider<CurrentUser> currentUserProvider) {
super(ServletScopes.REQUEST);
- this.urlProvider = urlProvider;
- this.remotePeerProvider = remotePeerProvider;
- this.currentUserProvider = currentUserProvider;
+ this.url = urlProvider != null ? urlProvider.get() : null;
+ this.peer = remotePeerProvider.get();
+ this.user = currentUserProvider.get();
}
/**
@@ -61,17 +61,14 @@
// Request scopes appear to use specific keys in their map, instead of only
// providers. Add bindings for both the key to the instance directly and the
// provider to the instance to be safe.
- String url = urlProvider.get();
seedMap.put(Key.get(typeOfProvider(String.class), CanonicalWebUrl.class),
Providers.of(url));
seedMap.put(Key.get(String.class, CanonicalWebUrl.class), url);
- SocketAddress peer = remotePeerProvider.get();
seedMap.put(Key.get(typeOfProvider(SocketAddress.class), RemotePeer.class),
Providers.of(peer));
seedMap.put(Key.get(SocketAddress.class, RemotePeer.class), peer);
- CurrentUser user = currentUserProvider.get();
seedMap.put(Key.get(typeOfProvider(CurrentUser.class)), Providers.of(user));
seedMap.put(Key.get(CurrentUser.class), user);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/ThreadLocalRequestScopePropagator.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/ThreadLocalRequestScopePropagator.java
index 403701a..581ccc1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/util/ThreadLocalRequestScopePropagator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/ThreadLocalRequestScopePropagator.java
@@ -74,6 +74,13 @@
/**
* Returns a new context object based on the passed in context that has no
* request scoped objects initialized.
+ * <p>
+ * Note that some code paths expect request-scoped objects like
+ * {@code CurrentUser} to be constructible starting from just the context
+ * object returned by this method. For example, in the SSH scope, the context
+ * includes the {@code SshSession}, which is used by
+ * {@code SshCurrentUserProvider} to construct a new {@code CurrentUser} in
+ * the new thread.
*
* @param ctx the context to continue.
* @return a new context.
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/tools/root/hooks/commit-msg b/gerrit-server/src/main/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
index 548f373..212ffb1 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
@@ -49,7 +49,25 @@
# Solaris AWK is just too broken
AWK=/usr/xpg4/bin/awk
fi
+
+ # How this works:
+ # - parse the commit message as (textLine+ blankLine*)*
+ # - assume textLine+ to be a footer until proven otherwise
+ # - exception: the first block is not footer (as it is the title)
+ # - read textLine+ into a variable
+ # - then count blankLines
+ # - once the next textLine appears, print textLine+ blankLine* as these
+ # aren't footer
+ # - in END, the last textLine+ block is available for footer parsing
$AWK '
+ BEGIN {
+ # while we start with the assumption that textLine+
+ # is a footer, the first block is not.
+ isFooter = 0
+ footerComment = 0
+ blankLines = 0
+ }
+
# Skip lines starting with "#" without any spaces before it.
/^#/ { next }
@@ -57,80 +75,78 @@
# up to the end of the file, assuming it is only patch data.
# If more than one line before the diff was empty, strip all but one.
/^diff --git a/ {
- if (blankLines > 1) {
- blankLines = 1
- }
+ blankLines = 0
while (getline) { }
next
}
- # Handle comments and continuations in tags ([foo: bar] etc)
- (caught == 1) && /^[ []/ {
- if (lines != "") {
- lines = lines "\n"
- }
- lines = lines $0
+ # Count blank lines outside footer comments
+ /^$/ && (footerComment == 0) {
+ blankLines++
next
}
- # Handle normal lines (ie. not starting with some tag like "Signed-off-by:").
- # If normal text appears after tags were "caught", handle them as normal text, too.
- # Also count blank lines in blankLines.
- !/^[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\// {
- if ($0 == "") {
- blankLines++
- next
- } else {
- for (i = 0; i < blankLines; i++) {
- print ""
- }
- blankLines = 0
- }
- if (caught == 1) {
- caught = 0
- print lines
- lines = ""
- }
- print $0
- next
+ # Catch footer comment
+ /^\[[a-zA-Z0-9-]+:/ && (isFooter == 1) {
+ footerComment = 1
}
- # Handle tags. They are "caught" and collected in the "lines" variable
+ /]$/ && (footerComment == 1) {
+ footerComment = 2
+ }
+
+ # We have a non-blank line after blank lines. Handle this.
+ (blankLines > 0) {
+ print lines
+ for (i = 0; i < blankLines; i++) {
+ print ""
+ }
+
+ lines = ""
+ blankLines = 0
+ isFooter = 1
+ footerComment = 0
+ }
+
+ # Detect that the current block is not the footer
+ (footerComment == 0) && (!/^\[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\//) {
+ isFooter = 0
+ }
+
{
- caught = 1
+ # We need this information about the current last comment line
+ if (footerComment == 2) {
+ footerComment = 0
+ }
if (lines != "") {
lines = lines "\n";
}
lines = lines $0
}
- # Tag handling:
- # If last line before tags was not blank, there were no tags.
- # In that case, print everything, plus a blank line, followed by Change-Id.
- # Otherwise there were tags. Look for the right place to inject Change-Id,
- # by considering CHANGE_ID_AFTER. Tags listed in it (case insensitive) come first,
+ # Footer handling:
+ # If the last block is considered a footer, splice in the Change-Id at the
+ # right place.
+ # Look for the right place to inject Change-Id by considering
+ # CHANGE_ID_AFTER. Keys listed in it (case insensitive) come first,
# then Change-Id, then everything else (eg. Signed-off-by:).
+ #
+ # Otherwise just print the last block, a new line and the Change-Id as a
+ # block of its own.
END {
unprinted = 1
- if (blankLines == 0) {
- if (lines == "") {
- print ""
- } else {
- print lines "\n"
+ if (isFooter == 0) {
+ print lines "\n"
+ lines = ""
+ }
+ changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):"
+ numlines = split(lines, footer, "\n")
+ for (line = 1; line <= numlines; line++) {
+ if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) {
+ unprinted = 0
+ print "Change-Id: I'"$id"'"
}
- } else {
- for (i = 0; i < blankLines; i++) {
- print ""
- }
- changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):"
- numlines = split(lines, footer, "\n")
- for (line = 1; line <= numlines; line++) {
- if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) {
- unprinted = 0
- print "Change-Id: I'"$id"'"
- }
- print footer[line]
- }
+ print footer[line]
}
if (unprinted) {
print "Change-Id: I'"$id"'"
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/tools/hooks/CommitMsgHookTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/tools/hooks/CommitMsgHookTest.java
index 0d84a93..17fe068 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/tools/hooks/CommitMsgHookTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/tools/hooks/CommitMsgHookTest.java
@@ -388,6 +388,25 @@
"git://example.com/ fixes this\n"));
}
+ @Test
+ public void testWithFalseTags() throws Exception {
+ assertEquals("foo\n" + //
+ "\n" + //
+ "FakeLine:\n" + //
+ " foo\n" + //
+ " bar\n" + //
+ "\n" + //
+ "Change-Id: I67632a37fd2e08a35f766f52fc9a47f4ea868c55\n" + //
+ "RealTag: abc\n", //
+ call("foo\n" + //
+ "\n" + //
+ "FakeLine:\n" + //
+ " foo\n" + //
+ " bar\n" + //
+ "\n" + //
+ "RealTag: abc\n"));
+ }
+
private void hookDoesNotModify(final String in) throws Exception {
assertEquals(in, call(in));
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
index 9bca0e5..d9a1c3f 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
@@ -66,6 +66,11 @@
processor.setIncludeCommitMessage(on);
}
+ @Option(name = "--dependencies", usage = "Include depends-on and needed-by information")
+ void setDependencies(boolean on) {
+ processor.setIncludeDependencies(on);
+ }
+
@Argument(index = 0, required = true, multiValued = true, metaVar = "QUERY", usage = "Query to execute")
private List<String> query;
diff --git a/pom.xml b/pom.xml
index 14db525..2520825 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,7 +46,7 @@
</issueManagement>
<properties>
- <jgitVersion>1.3.0.201202151440-r.54-gd725ecb</jgitVersion>
+ <jgitVersion>1.3.0.201202151440-r.75-gff13648</jgitVersion>
<gwtormVersion>1.4</gwtormVersion>
<gwtjsonrpcVersion>1.2.5</gwtjsonrpcVersion>
<gwtexpuiVersion>1.2.5</gwtexpuiVersion>