Merge "Access control documentation: Tidying up format mistake"
diff --git a/Documentation/cmd-index.txt b/Documentation/cmd-index.txt
index 55cb456..e446f71 100644
--- a/Documentation/cmd-index.txt
+++ b/Documentation/cmd-index.txt
@@ -150,6 +150,9 @@
link:cmd-plugin-remove.html[gerrit plugin rm]::
Alias for 'gerrit plugin remove'.
+link:cmd-test-submit-rule.html[gerrit test-submit-rule]::
+ Test prolog submit rules.
+
link:cmd-kill.html[kill]::
Kills a scheduled or running task.
diff --git a/Documentation/cmd-test-submit-rule.txt b/Documentation/cmd-test-submit-rule.txt
new file mode 100644
index 0000000..5b70bd1
--- /dev/null
+++ b/Documentation/cmd-test-submit-rule.txt
@@ -0,0 +1,93 @@
+gerrit test-submit-rule
+=======================
+
+NAME
+----
+gerrit test-submit-rule - Test prolog submit rules with a chosen changeset.
+
+SYNOPSIS
+--------
+[verse]
+'ssh' -p <port> <host> 'gerrit test-submit-rule'
+ [-s]
+ [--no-filters]
+ [--format {TEXT | JSON}]
+ CHANGE
+
+DESCRIPTION
+-----------
+Provides a way to test prolog link:prolog-cookbook.html[submit rules].
+
+OPTIONS
+-------
+-s::
+ Reads a rules.pl file from stdin instead of rules.pl in refs/meta/config.
+
+--no-filters::
+ Don't run the submit_filter/2 from the parent projects of the specified change.
+
+--format::
+ What output format to display the results in.
++
+--
+`text`:: Simple text based format.
+`json`:: A JSON object described in link:json.html#submitRecord[submit record].
+`json_compact`:: Minimized JSON output.
+--
+
+ACCESS
+------
+Can be used by anyone that has permission to read the specified changeset.
+
+EXAMPLES
+--------
+
+
+Test submit_rule from stdin.
+====
+ $ cat non-author-codereview.pl | ssh -p 29418 review.example.com gerrit test-submit-rule -s I78f2c6673db24e4e92ed32f604c960dc952437d9
+ Non-Author-Code-Review: NOT_READY
+ Verified: NOT_READY
+ Code-Review: NOT_READY by Anonymous Coward <test@email.com>
+
+ NOT_READY
+====
+
+Test submit_rule from stdin and return the results as JSON.
+====
+ cat non-author-codereview.pl | ssh -p 29418 review.example.com gerrit test-submit-rule --format=JSON -s I78f2c6673db24e4e92ed32f604c960dc952437d9
+ {
+ "approvals": [
+ {
+ "type": "Verified",
+ "value": "NEED"
+ },
+ {
+ "type": "Code-Review",
+ "value": "OK",
+ "by": {
+ "email": "test@email.com",
+ "username": "test"
+ }
+ }
+ ],
+ "value": "NOT_READY"
+ }
+====
+
+Test the active submit_rule from the refs/meta/config branch, ignoring filters in the project parents.
+====
+ $ ssh -p 29418 review.example.com gerrit test-submit-rule I78f2c6673db24e4e92ed32f604c960dc952437d9 --no-filters
+ Verified: NOT_READY
+ Code-Review: NOT_READY by Anonymous Coward <test@email.com>
+
+ NOT_READY
+====
+
+SCRIPTING
+---------
+Can be used either interactively for testing new prolog submit rules, or from a script to check the submit status of a change.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index c328b41..ea45d804e 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1707,6 +1707,18 @@
Default is `(memberUid=${username})` for RFC 2307,
and unset (disabled) for Active Directory.
+[[ldap.groupName]]ldap.groupName::
++
+_(Optional)_ Name of an attribute on the group object which contains
+the value for the group name in Gerrit.
+Typically this is the `cn` property in LDAP, but could also be
+`apple-group-realname`.
++
+Attribute values may be concatenated with literal strings. For example
+to join group name and group id, use the pattern `${cn} (${gidNumber})`.
++
+Default is `cn` for RFC 2307 servers and Active Directory.
+
[[ldap.localUsernameToLowerCase]]ldap.localUsernameToLowerCase::
+
Converts the local username, that is used to login into the Gerrit
diff --git a/Documentation/json.txt b/Documentation/json.txt
index f0588ce..aea9bac 100644
--- a/Documentation/json.txt
+++ b/Documentation/json.txt
@@ -30,6 +30,9 @@
commitMessage:: The full commit message for the change.
+createdOn:: Time in seconds since the UNIX epoch when this change
+was created.
+
lastUpdated:: Time in seconds since the UNIX epoch when this change
was last updated.
@@ -48,14 +51,20 @@
ABANDONED;; Change was abandoned by its owner or administrator.
+comments:: All comments for this change in <<message,message attributes>>.
+
trackingIds:: Issue tracking system links in
-<<trackingid,trackingid attribute>>, scraped out of the commit
+<<trackingid,trackingid attributes>>, scraped out of the commit
message based on the server's
link:config-gerrit.html#trackingid[trackingid] sections.
currentPatchSet:: Current <<patchSet,patchSet attribute>>.
-patchSets:: All <<patchSet,patchSet attribute>> for this change.
+patchSets:: All <<patchSet,patchSet attributes>> for this change.
+
+dependsOn:: List of changes that this change depends on in <<dependency,dependency attributes>>.
+
+neededBy:: List of changes that depend on this change in <<dependency,dependency attributes>>.
submitRecords:: The <<submitRecord,submitRecord attribute>> contains
information about whether this change has been or can be submitted.
@@ -90,14 +99,23 @@
revision:: Git commit for this patchset.
+parents:: List of parent revisions.
+
ref:: Git reference pointing at the revision. This reference is
available through the Gerrit Code Review server's Git interface
for the containing change.
uploader:: Uploader of the patch set in <<account,account attribute>>.
+createdOn:: Time in seconds since the UNIX epoch when this patchset
+was created.
+
approvals:: The <<approval,approval attribute>> granted.
+comments:: All inline comments for this patchset in <<patchsetcomment,patchsetComment attributes>>.
+
+files:: All changed files in this patchset in <<patch,patch attributes>>.
+
[[approval]]
approval
--------
@@ -123,10 +141,10 @@
newRev:: The new value the ref was updated to.
-project:: Project path in Gerrit.
-
refName:: Ref name within project.
+project:: Project path in Gerrit.
+
[[queryLimit]]
queryLimit
----------
@@ -178,6 +196,68 @@
by:: The <<account,account>> that applied the label.
+[[dependency]]
+dependency
+----------
+Information about a change or patchset dependency.
+
+id:: Change identifier.
+
+number:: Change number.
+
+revision:: Patchset revision.
+
+ref:: Ref name.
+
+isCurrentPatchSet:: If the revision is the current patchset of the change.
+
+[[message]]
+message
+-------
+Comment added on a change by a reviewer.
+
+timestamp:: Time in seconds since the UNIX epoch when this comment
+was added.
+
+reviewer:: The <<account,account>> that added the comment.
+
+message:: The comment text.
+
+[[patchsetcomment]]
+patchsetComment
+---------------
+Comment added inline on a patchset by a reviewer.
+
+file:: The name of the file on which the comment was added.
+
+line:: The line number at which the comment was added.
+
+reviewer:: The <<account,account>> that added the comment.
+
+message:: The comment text.
+
+[[patch]]
+patch
+-----
+Information about a patch on a file.
+
+file:: The name of the file.
+
+type:: The type of change.
+
+ ADDED;; The file is being created/introduced by this patch.
+
+ MODIFIED;; The file already exists, and has updated content.
+
+ DELETED;; The file existed, but is being removed by this patch.
+
+ RENAMED;; The file is renamed.
+
+ COPIED;; The file is copied from another file.
+
+ REWRITE;; Sufficient amount of content changed to claim the file was rewritten.
+
+
SEE ALSO
--------
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
index 5fd0d39..f334b36 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
@@ -16,6 +16,7 @@
import static com.google.gerrit.common.data.GlobalCapability.ADMINISTRATE_SERVER;
import static com.google.gerrit.common.data.GlobalCapability.CREATE_PROJECT;
+import static com.google.gerrit.common.data.GlobalCapability.CREATE_GROUP;
import com.google.gerrit.client.account.AccountCapabilities;
import com.google.gerrit.client.auth.openid.OpenIdSignInDialog;
@@ -610,17 +611,28 @@
menuLeft.add(projectsBar, C.menuProjects());
if (signedIn) {
- final LinkMenuBar menuBar = new LinkMenuBar();
- addLink(menuBar, C.menuGroups(), PageLinks.ADMIN_GROUPS);
+ final LinkMenuBar groupsBar = new LinkMenuBar();
+ addLink(groupsBar, C.menuGroupsList(), PageLinks.ADMIN_GROUPS);
+ AccountCapabilities.all(new GerritCallback<AccountCapabilities>() {
+ @Override
+ public void onSuccess(AccountCapabilities result) {
+ if (result.canPerform(CREATE_GROUP)) {
+ addLink(groupsBar, C.menuGroupsCreate(), PageLinks.ADMIN_CREATE_GROUP);
+ }
+ }
+ }, CREATE_GROUP);
+ menuLeft.add(groupsBar, C.menuGroups());
+
+ final LinkMenuBar pluginsBar = new LinkMenuBar();
AccountCapabilities.all(new GerritCallback<AccountCapabilities>() {
@Override
public void onSuccess(AccountCapabilities result) {
if (result.canPerform(ADMINISTRATE_SERVER)) {
- addLink(menuBar, C.menuPlugins(), PageLinks.ADMIN_PLUGINS);
+ addLink(pluginsBar, C.menuPluginsInstalled(), PageLinks.ADMIN_PLUGINS);
+ menuLeft.add(pluginsBar, C.menuPlugins());
}
}
}, ADMINISTRATE_SERVER);
- menuLeft.add(menuBar, C.menuAdmin());
}
if (getConfig().isDocumentationAvailable()) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
index dbf9973..331ceb6 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
@@ -73,10 +73,12 @@
String menuProjectsList();
String menuProjectsCreate();
- String menuAdmin();
String menuPeople();
String menuGroups();
+ String menuGroupsList();
+ String menuGroupsCreate();
String menuPlugins();
+ String menuPluginsInstalled();
String menuDocumentation();
String menuDocumentationIndex();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
index 41dc06b..ef23c8d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
@@ -56,10 +56,13 @@
menuProjectsList = List
menuProjectsCreate = Create New Project
-menuAdmin = Admin
menuPeople = People
menuGroups = Groups
+menuGroupsList = List
+menuGroupsCreate = Create New Group
+
menuPlugins = Plugins
+menuPluginsInstalled = Installed
menuDocumentation = Documentation
menuDocumentationIndex = Index
@@ -90,4 +93,4 @@
projectAccessError = You don't have permissions to modify the access rights for the following refs:
projectAccessProposeForReviewHint = You may propose these modifications to the project owners by clicking on 'Save for Review'.
-userCannotVoteToolTip = User cannot vote in this category
\ No newline at end of file
+userCannotVoteToolTip = User cannot vote in this category
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
index 9e65836..0c18169 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
@@ -108,7 +108,6 @@
String projectAdminTabAccess();
String plugins();
- String pluginTabInstalled();
String pluginDisabled();
String columnPluginName();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
index 47bcea6..91202c5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
@@ -88,7 +88,6 @@
projectAdminTabAccess = Access
plugins = Plugins
-pluginTabInstalled = Installed
pluginDisabled = Disabled
columnPluginName = Plugin Name
columnPluginVersion = Version
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java
index dc58e22..3157d97 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java
@@ -14,16 +14,12 @@
package com.google.gerrit.client.admin;
-import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.rpc.ScreenLoadCallback;
import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gerrit.client.ui.Hyperlink;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.GroupList;
-import com.google.gwt.user.client.ui.VerticalPanel;
public class GroupListScreen extends AccountScreen {
- private VerticalPanel createGroupLinkPanel;
private GroupTable groups;
@Override
@@ -33,7 +29,6 @@
.visibleGroups(new ScreenLoadCallback<GroupList>(this) {
@Override
protected void preDisplay(GroupList result) {
- createGroupLinkPanel.setVisible(result.isCanCreateGroup());
groups.display(result.getGroups());
groups.finishDisplay();
}
@@ -45,12 +40,6 @@
super.onInitUI();
setPageTitle(Util.C.groupListTitle());
- createGroupLinkPanel = new VerticalPanel();
- createGroupLinkPanel.setStyleName(Gerrit.RESOURCES.css().createGroupLink());
- createGroupLinkPanel.add(new Hyperlink(Util.C.headingCreateGroup(),
- PageLinks.ADMIN_CREATE_GROUP));
- add(createGroupLinkPanel);
-
groups = new GroupTable(true /* hyperlink to admin */, PageLinks.ADMIN_GROUPS);
add(groups);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginScreen.java
index 72cd7f9..dabcb45 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginScreen.java
@@ -14,16 +14,12 @@
package com.google.gerrit.client.admin;
-import static com.google.gerrit.common.PageLinks.ADMIN_PLUGINS;
+import com.google.gerrit.client.ui.Screen;
-import com.google.gerrit.client.ui.MenuScreen;
-
-public abstract class PluginScreen extends MenuScreen {
+public abstract class PluginScreen extends Screen {
public PluginScreen() {
setRequiresSignIn(true);
-
- link(Util.C.pluginTabInstalled(), ADMIN_PLUGINS);
}
@Override
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
index 95b8487f..c0f0c4b 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
@@ -140,8 +140,9 @@
throw (Die) why;
}
- final StringBuilder buf = new StringBuilder();
+ final StringBuilder buf = new StringBuilder(ce.getMessage());
while (why != null) {
+ buf.append("\n");
buf.append(why.getMessage());
why = why.getCause();
if (why != null) {
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Patch.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Patch.java
index 00e282b..6ddd6d2 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Patch.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Patch.java
@@ -84,7 +84,7 @@
/** Path was copied from {@link Patch#getSourceFileName()}. */
COPIED('C'),
- /** Sufficient amount of content changed to claim the file was written. */
+ /** Sufficient amount of content changed to claim the file was rewritten. */
REWRITE('W');
private final char code;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
index 2265bc2..7cf6268 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
@@ -242,6 +242,7 @@
final List<String> groupBases;
final SearchScope groupScope;
final ParameterizedString groupPattern;
+ final ParameterizedString groupName;
final List<LdapQuery> groupMemberQueryList;
LdapSchema(final DirContext ctx) {
@@ -257,6 +258,7 @@
groupBases = LdapRealm.optionalList(config, "groupBase");
groupScope = LdapRealm.scope(config, "groupScope");
groupPattern = LdapRealm.paramString(config, "groupPattern", type.groupPattern());
+ groupName = LdapRealm.paramString(config, "groupName", type.groupName());
final String groupMemberPattern =
LdapRealm.optdef(config, "groupMemberPattern", type.groupMemberPattern());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
index 5c30e5c..6b54f6b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
@@ -41,6 +41,7 @@
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -81,11 +82,11 @@
return uuid.get().startsWith(LDAP_UUID);
}
- private static GroupReference groupReference(LdapQuery.Result res)
- throws NamingException {
+ private static GroupReference groupReference(ParameterizedString p,
+ LdapQuery.Result res) throws NamingException {
return new GroupReference(
new AccountGroup.UUID(LDAP_UUID + res.getDN()),
- LDAP_NAME + cnFor(res.getDN()));
+ LDAP_NAME + LdapRealm.apply(p, res));
}
private static String cnFor(String dn) {
@@ -203,13 +204,14 @@
LdapSchema schema = helper.getSchema(ctx);
ParameterizedString filter = ParameterizedString.asis(
schema.groupPattern.replace(GROUPNAME, name).toString());
- Set<String> returnAttrs = Collections.<String>emptySet();
+ Set<String> returnAttrs =
+ new HashSet<String>(schema.groupName.getParameterNames());
Map<String, String> params = Collections.emptyMap();
for (String groupBase : schema.groupBases) {
LdapQuery query = new LdapQuery(
groupBase, schema.groupScope, filter, returnAttrs);
for (LdapQuery.Result res : query.query(ctx, params)) {
- out.add(groupReference(res));
+ out.add(groupReference(schema.groupName, res));
}
}
} finally {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
index 72eb7ec..4ddbc67 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
@@ -167,7 +167,7 @@
return !readOnlyAccountFields.contains(field);
}
- private static String apply(ParameterizedString p, LdapQuery.Result m)
+ static String apply(ParameterizedString p, LdapQuery.Result m)
throws NamingException {
if (p == null) {
return null;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapType.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapType.java
index 3c4a54b..fd5bb30 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapType.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapType.java
@@ -36,6 +36,8 @@
abstract String groupMemberPattern();
+ abstract String groupName();
+
abstract String accountFullName();
abstract String accountEmailAddress();
@@ -58,6 +60,11 @@
}
@Override
+ String groupName() {
+ return "cn";
+ }
+
+ @Override
String accountFullName() {
return "displayName";
}
@@ -101,6 +108,11 @@
}
@Override
+ String groupName() {
+ return "cn";
+ }
+
+ @Override
String groupMemberPattern() {
return null; // Active Directory uses memberOf in the account
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
index c0e00aa..79e35be 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
@@ -92,7 +92,8 @@
banCommitNotes.set(commitToBan, createNoteContent(reason, inserter));
}
inserter.flush();
- NotesBranchUtil notesBranchUtil = notesBranchUtilFactory.create(repo);
+ NotesBranchUtil notesBranchUtil = notesBranchUtilFactory.create(repo,
+ inserter);
NoteMap newlyCreated =
notesBranchUtil.commitNewNotes(banCommitNotes, REF_REJECT_COMMITS,
createPersonIdent(), buildCommitMessage(commitsToBan, reason));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java
index 6d1b155..eab27f0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java
@@ -89,6 +89,7 @@
});
}
+ @SuppressWarnings("unused")
@Provides
public PerThreadRequestScope.Scoper provideScoper(
final PerThreadRequestScope.Propagator propagator) {
@@ -243,6 +244,7 @@
dest = d;
}
+ @Override
public void run() {
unschedule(this);
mergeImpl(dest);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java
index b067a49..50412e3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java
@@ -114,9 +114,11 @@
message.append("* ").append(c.getShortMessage()).append("\n");
}
- NotesBranchUtil notesBranchUtil = notesBranchUtilFactory.create(db);
+ NotesBranchUtil notesBranchUtil = notesBranchUtilFactory.create(db,
+ inserter);
notesBranchUtil.commitAllNotes(notes, REFS_NOTES_REVIEW, author,
message.toString());
+ inserter.flush();
} catch (IOException e) {
throw new CodeReviewNoteCreationException(e);
} catch (ConcurrentRefUpdateException e) {
@@ -148,9 +150,11 @@
notes.set(commitId, createNoteContent(c, commitId));
}
- NotesBranchUtil notesBranchUtil = notesBranchUtilFactory.create(db);
+ NotesBranchUtil notesBranchUtil = notesBranchUtilFactory.create(db,
+ inserter);
notesBranchUtil.commitAllNotes(notes, REFS_NOTES_REVIEW, author,
commitMessage);
+ inserter.flush();
} catch (ConcurrentRefUpdateException e) {
throw new CodeReviewNoteCreationException(e);
} finally {
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 26fd2ea..8123c64 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
@@ -487,6 +487,7 @@
toMerge.clear();
toMerge.addAll(heads);
Collections.sort(toMerge, new Comparator<CodeReviewCommit>() {
+ @Override
public int compare(final CodeReviewCommit a, final CodeReviewCommit b) {
return a.originalOrder - b.originalOrder;
}
@@ -854,6 +855,7 @@
approvalList =
db.patchSetApprovals().byPatchSet(n.patchsetId).toList();
Collections.sort(approvalList, new Comparator<PatchSetApproval>() {
+ @Override
public int compare(final PatchSetApproval a, final PatchSetApproval b) {
return a.getGranted().compareTo(b.getGranted());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/NotesBranchUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/NotesBranchUtil.java
index 17cfea8..7887edd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/NotesBranchUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/NotesBranchUtil.java
@@ -47,7 +47,7 @@
*/
public class NotesBranchUtil {
public interface Factory {
- NotesBranchUtil create(Repository db);
+ NotesBranchUtil create(Repository db, ObjectInserter inserter);
}
private static final int MAX_LOCK_FAILURE_CALLS = 10;
@@ -55,6 +55,7 @@
private PersonIdent gerritIdent;
private final Repository db;
+ private final ObjectInserter inserter;
private RevCommit baseCommit;
private NoteMap base;
@@ -63,7 +64,6 @@
private NoteMap ours;
private RevWalk revWalk;
- private ObjectInserter inserter;
private ObjectReader reader;
private boolean overwrite;
@@ -71,9 +71,11 @@
@Inject
public NotesBranchUtil(@GerritPersonIdent final PersonIdent gerritIdent,
- @Assisted Repository db) {
+ @Assisted Repository db,
+ @Assisted ObjectInserter inserter) {
this.gerritIdent = gerritIdent;
this.db = db;
+ this.inserter = inserter;
}
/**
@@ -128,7 +130,6 @@
ConcurrentRefUpdateException {
try {
revWalk = new RevWalk(db);
- inserter = db.newObjectInserter();
reader = db.newObjectReader();
loadBase(notesBranch);
if (overwrite) {
@@ -144,7 +145,6 @@
updateRef(notesBranch);
} finally {
revWalk.release();
- inserter.release();
reader.release();
}
}
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 9832a79..033faaa 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
@@ -443,12 +443,18 @@
return ruleError("Project submit rule has no solution");
}
- // Convert the results from Prolog Cafe's format to Gerrit's common format.
- // can_submit/1 terminates when an ok(P) record is found. Therefore walk
- // the results backwards, using only that ok(P) record if it exists. This
- // skips partial results that occur early in the output. Later after the loop
- // the out collection is reversed to restore it to the original ordering.
- //
+ return resultsToSubmitRecord(submitRule, results);
+ }
+
+ /**
+ * Convert the results from Prolog Cafe's format to Gerrit's common format.
+ *
+ * can_submit/1 terminates when an ok(P) record is found. Therefore walk
+ * the results backwards, using only that ok(P) record if it exists. This
+ * skips partial results that occur early in the output. Later after the loop
+ * the out collection is reversed to restore it to the original ordering.
+ */
+ public List<SubmitRecord> resultsToSubmitRecord(Term submitRule, List<Term> results) {
List<SubmitRecord> out = new ArrayList<SubmitRecord>(results.size());
for (int resultIdx = results.size() - 1; 0 <= resultIdx; resultIdx--) {
Term submitRecord = results.get(resultIdx);
@@ -565,7 +571,7 @@
&& who.arg(0).isInteger();
}
- private static Term toListTerm(List<Term> terms) {
+ public static Term toListTerm(List<Term> terms) {
Term list = Prolog.Nil;
for (int i = terms.size() - 1; i >= 0; i--) {
list = new ListTerm(terms.get(i), list);
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 212ffb1..06926df 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
@@ -17,6 +17,8 @@
# limitations under the License.
#
+unset GREP_OPTIONS
+
CHANGE_ID_AFTER="Bug|Issue"
MSG="$1"
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/MasterCommandModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/MasterCommandModule.java
index db4e985..90bc07e 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/MasterCommandModule.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/MasterCommandModule.java
@@ -31,6 +31,7 @@
command(gerrit, "rename-group").to(RenameGroupCommand.class);
command(gerrit, "create-project").to(CreateProjectCommand.class);
command(gerrit, "gsql").to(AdminQueryShell.class);
+ command(gerrit, "test-submit-rule").to(TestSubmitRule.class);
command(gerrit, "set-reviewers").to(SetReviewersCommand.class);
command(gerrit, "receive-pack").to(Receive.class);
command(gerrit, "set-project-parent").to(AdminSetParent.class);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitRule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitRule.java
new file mode 100644
index 0000000..9a81140
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitRule.java
@@ -0,0 +1,240 @@
+// 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.sshd.commands;
+
+import com.google.gerrit.common.data.AccountInfo;
+import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.rules.PrologEnvironment;
+import com.google.gerrit.rules.StoredValues;
+import com.google.gerrit.server.OutputFormat;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.config.AnonymousCowardName;
+import com.google.gerrit.server.events.AccountAttribute;
+import com.google.gerrit.server.events.SubmitLabelAttribute;
+import com.google.gerrit.server.events.SubmitRecordAttribute;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.sshd.SshCommand;
+import com.google.gson.reflect.TypeToken;
+import com.google.inject.Inject;
+
+import com.googlecode.prolog_cafe.compiler.CompileException;
+import com.googlecode.prolog_cafe.lang.BufferingPrologControl;
+import com.googlecode.prolog_cafe.lang.JavaObjectTerm;
+import com.googlecode.prolog_cafe.lang.ListTerm;
+import com.googlecode.prolog_cafe.lang.Prolog;
+import com.googlecode.prolog_cafe.lang.PrologClassLoader;
+import com.googlecode.prolog_cafe.lang.PrologException;
+import com.googlecode.prolog_cafe.lang.PrologMachineCopy;
+import com.googlecode.prolog_cafe.lang.SymbolTerm;
+import com.googlecode.prolog_cafe.lang.Term;
+import com.googlecode.prolog_cafe.lang.VariableTerm;
+
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/** Command that allows testing of prolog submit-rules in a live instance. */
+final class TestSubmitRule extends SshCommand {
+ @Inject
+ private ReviewDb db;
+
+ @Inject
+ private PrologEnvironment.Factory envFactory;
+
+ @Inject
+ private ChangeControl.Factory ccFactory;
+
+ @Inject
+ private AccountCache accountCache;
+
+ final @AnonymousCowardName String anonymousCowardName;
+
+ @Argument(index = 0, required = true, usage = "ChangeId to load in prolog environment")
+ private String changeId;
+
+ @Option(name = "-s",
+ usage = "Read prolog script from stdin instead of reading rules.pl from the refs/meta/config branch")
+ private boolean useStdin;
+
+ @Option(name = "--format", metaVar = "FMT", usage = "Output display format")
+ private OutputFormat format = OutputFormat.TEXT;
+
+ @Option(name = "--no-filters", aliases = {"-n"},
+ usage = "Don't run the submit_filter/2 from the parent projects")
+ private boolean skipSubmitFilters;
+
+ private static final String[] PACKAGE_LIST = {Prolog.BUILTIN, "gerrit"};
+
+ @Inject
+ public TestSubmitRule(@AnonymousCowardName String anonymous) {
+ anonymousCowardName = anonymous;
+ }
+ private PrologMachineCopy newMachine() {
+ BufferingPrologControl ctl = new BufferingPrologControl();
+ ctl.setMaxDatabaseSize(16 * 1024);
+ ctl.setPrologClassLoader(new PrologClassLoader(getClass().getClassLoader()));
+ return PrologMachineCopy.save(ctl);
+ }
+
+ @Override
+ protected void run() throws UnloggedFailure {
+ InputStreamReader inReader = new InputStreamReader(in);
+
+ try {
+ PrologEnvironment pcl;
+
+ List<Change> changeList =
+ db.changes().byKey(new Change.Key(changeId)).toList();
+ if (changeList.size() != 1)
+ throw new UnloggedFailure(1, "Invalid ChangeId");
+
+ Change c = changeList.get(0);
+ PatchSet ps = db.patchSets().get(c.currentPatchSetId());
+ // Will throw exception if current user can not access this change, and
+ // thus will leak information that a change-id is valid even though the
+ // user are not allowed to see the change.
+ // See http://code.google.com/p/gerrit/issues/detail?id=1586
+ ChangeControl cc = ccFactory.controlFor(c);
+ ProjectState projectState = cc.getProjectControl().getProjectState();
+
+ if (useStdin) {
+ pcl = envFactory.create(newMachine());
+ } else {
+ pcl = projectState.newPrologEnvironment();
+ }
+
+ pcl.set(StoredValues.REVIEW_DB, db);
+ pcl.set(StoredValues.CHANGE, c);
+ pcl.set(StoredValues.PATCH_SET, ps);
+ pcl.set(StoredValues.CHANGE_CONTROL, cc);
+ if (useStdin) {
+ pcl.initialize(PACKAGE_LIST);
+ pcl.execute(Prolog.BUILTIN, "consult_stream",
+ SymbolTerm.intern("stdin"), new JavaObjectTerm(inReader));
+ }
+
+ List<Term> results = new ArrayList<Term>();
+ Term submitRule =
+ pcl.once("gerrit", "locate_submit_rule", new VariableTerm());
+
+ for (Term[] template : pcl.all("gerrit", "can_submit", submitRule,
+ new VariableTerm())) {
+ results.add(template[1]);
+ }
+
+ if (!skipSubmitFilters) {
+ runSubmitFilters(projectState, results, pcl);
+ }
+
+ List<SubmitRecord> res = cc.resultsToSubmitRecord(submitRule, results);
+ for (SubmitRecord r : res) {
+ if (format.isJson()) {
+ SubmitRecordAttribute submitRecord = new SubmitRecordAttribute();
+ submitRecord.status = r.status.name();
+
+ List<SubmitLabelAttribute> submitLabels = new LinkedList<SubmitLabelAttribute>();
+ for(SubmitRecord.Label l : r.labels) {
+ SubmitLabelAttribute label = new SubmitLabelAttribute();
+ label.label = l.label;
+ label.status= l.status.name();
+ if(l.appliedBy != null) {
+ Account a = accountCache.get(l.appliedBy).getAccount();
+ label.by = new AccountAttribute();
+ label.by.email = a.getPreferredEmail();
+ label.by.name = a.getFullName();
+ label.by.username = a.getUserName();
+ }
+ submitLabels.add(label);
+ }
+ submitRecord.labels = submitLabels;
+ format.newGson().toJson(submitRecord, new TypeToken<SubmitRecordAttribute>() {}.getType(), stdout);
+ stdout.print('\n');
+ } else {
+ for(SubmitRecord.Label l : r.labels) {
+ stdout.print(l.label + ": " + l.status);
+ if(l.appliedBy != null) {
+ AccountInfo a = new AccountInfo(accountCache.get(l.appliedBy).getAccount());
+ stdout.print(" by " + a.getNameEmail(anonymousCowardName));
+ }
+ stdout.print('\n');
+ }
+ stdout.print("\n" + r.status.name() + "\n");
+ }
+ }
+ } catch (Exception e) {
+ throw new UnloggedFailure("Processing of prolog script failed: " + e);
+ }
+ }
+
+ private void runSubmitFilters(ProjectState projectState, List<Term> results,
+ PrologEnvironment pcl) throws UnloggedFailure {
+ ProjectState parentState = projectState.getParentState();
+ PrologEnvironment childEnv = pcl;
+ Set<Project.NameKey> projectsSeen = new HashSet<Project.NameKey>();
+ projectsSeen.add(projectState.getProject().getNameKey());
+
+ while (parentState != null) {
+ if (!projectsSeen.add(parentState.getProject().getNameKey())) {
+ // parent has been seen before, stop walk up inheritance tree
+ break;
+ }
+ PrologEnvironment parentEnv;
+ try {
+ parentEnv = parentState.newPrologEnvironment();
+ } catch (CompileException err) {
+ throw new UnloggedFailure("Cannot consult rules.pl for "
+ + parentState.getProject().getName() + err);
+ }
+
+ parentEnv.copyStoredValues(childEnv);
+ Term filterRule =
+ parentEnv.once("gerrit", "locate_submit_filter", new VariableTerm());
+ if (filterRule != null) {
+ try {
+ Term resultsTerm = ChangeControl.toListTerm(results);
+ results.clear();
+ Term[] template =
+ parentEnv.once("gerrit", "filter_submit_results", filterRule,
+ resultsTerm, new VariableTerm());
+ @SuppressWarnings("unchecked")
+ final List<? extends Term> termList =
+ ((ListTerm) template[2]).toJava();
+ results.addAll(termList);
+ } catch (PrologException err) {
+ throw new UnloggedFailure("Exception calling " + filterRule + " of "
+ + parentState.getProject().getName() + err);
+ } catch (RuntimeException err) {
+ throw new UnloggedFailure("Exception calling " + filterRule + " of "
+ + parentState.getProject().getName() + err);
+ }
+ }
+
+ parentState = parentState.getParentState();
+ childEnv = parentEnv;
+ }
+ }
+}