Merge "Cleanup unused RefConfigSection.REF_CONFIG"
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index dc7d1b4..fc10623 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -247,6 +247,22 @@
 JSON Entities
 -------------
 
+[[account-info]]
+AccountInfo
+~~~~~~~~~~~
+The `AccountInfo` entity contains information about an account.
+
+[options="header",width="50%",cols="1,^1,5"]
+|===========================
+|Field Name    ||Description
+|`_account_id` ||The numeric ID of the account.
+|`name`        |optional|The full name of the user. +
+Only set if detailed account information is requested.
+|`email`       |optional|
+The email address the user prefers to be contacted through. +
+Only set if detailed account information is requested.
+|===========================
+
 [[capability-info]]
 CapabilityInfo
 ~~~~~~~~~~~~~~
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index e744488..f360482 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -1540,24 +1540,6 @@
 change.
 |===========================
 
-[[account-info]]
-AccountInfo
-~~~~~~~~~~~
-The `AccountInfo` entity contains information about an account.
-
-[options="header",width="50%",cols="1,^1,5"]
-|===========================
-|Field Name    ||Description
-|`_account_id` ||The numeric ID of the account.
-|`name`        |optional|The full name of the user. +
-Only set if link:#detailed-accounts[detailed account information] is
-requested.
-|`email`       |optional|
-The email address the user prefers to be contacted through. +
-Only set if link:#detailed-accounts[detailed account information] is
-requested.
-|===========================
-
 [[add-reviewer-result]]
 AddReviewerResult
 ~~~~~~~~~~~~~~~~~
@@ -1584,7 +1566,8 @@
 The `ApprovalInfo` entity contains information about an approval from a
 user for a label on a change.
 
-`ApprovalInfo` has the same fields as link:#account-info[AccountInfo].
+`ApprovalInfo` has the same fields as
+link:rest-api-accounts.html#account-info[AccountInfo].
 In addition `ApprovalInfo` has the following fields:
 
 [options="header",width="50%",cols="1,^1,5"]
@@ -1633,7 +1616,8 @@
 |`_sortkey`           ||The sortkey of the change.
 |`_number`            ||The legacy numeric ID of the change.
 |`owner`              ||
-The owner of the change as an link:#account-info[AccountInfo] entity.
+The owner of the change as an link:rest-api-accounts.html#account-info[
+AccountInfo] entity.
 |`labels`             |optional|
 The labels of the change as a map that maps the label names to
 link:#label-info[LabelInfo] entries. +
@@ -1645,7 +1629,7 @@
 Only set if link:#detailed-labels[detailed labels] are requested.
 |`removable_reviewers`|optional|
 The reviewers that can be removed by the calling user as a list of
-link:#account-info[AccountInfo] entities. +
+link:rest-api-accounts.html#account-info[AccountInfo] entities. +
 Only set if link:#detailed-labels[detailed labels] are requested.
 |`current_revision`   |optional|
 The commit ID of the current patch set of this change. +
@@ -1809,16 +1793,16 @@
 |===========================
 |Field Name    ||Description
 |`approved`    |optional|The user who approved this label on the change
-as a link:#account-info[AccountInfo] entity. +
+as a link:rest-api-accounts.html#account-info[AccountInfo] entity. +
 Only set if link:#labels[labels] are requested.
 |`rejected`    |optional|The user who rejected this label on the change
-as a link:#account-info[AccountInfo] entity. +
+as a link:rest-api-accounts.html#account-info[AccountInfo] entity. +
 Only set if link:#labels[labels] are requested.
 |`recommended` |optional|The user who recommended this label on the
-change as a link:#account-info[AccountInfo] entity. +
+change as a link:rest-api-accounts.html#account-info[AccountInfo] entity. +
 Only set if link:#labels[labels] are requested.
 |`disliked`    |optional|The user who disliked this label on the change
-as a link:#account-info[AccountInfo] entity. +
+as a link:rest-api-accounts.html#account-info[AccountInfo] entity. +
 Only set if link:#labels[labels] are requested.
 |`value`       |optional|The voting value of the user who
 recommended/disliked this label on the change if it is not
@@ -1919,8 +1903,9 @@
 The `ReviewerInfo` entity contains information about a reviewer and its
 votes on a change.
 
-`ReviewerInfo` has the same fields as link:#account-info[AccountInfo]
-and includes link:#detailed-accounts[detailed account information].
+`ReviewerInfo` has the same fields as
+link:rest-api-accounts.html#account-info[AccountInfo] and includes
+link:#detailed-accounts[detailed account information].
 In addition `ReviewerInfo` has the following fields:
 
 [options="header",width="50%",cols="1,6"]
@@ -2039,16 +2024,19 @@
 `CLOSED`, closed changes cannot be submitted. +
 `RULE_ERROR`, rule code failed with an error.
 |`ok`|optional|
-Map of labels that are approved; an link:#account-info[AccountInfo]
-identifies the voter chosen by the rule.
+Map of labels that are approved; an
+link:rest-api-accounts.html#account-info[AccountInfo] identifies the
+voter chosen by the rule.
 |`reject`|optional|
-Map of labels that are preventing submit; AccountInfo identifies voter.
+Map of labels that are preventing submit;
+link:rest-api-accounts.html#account-info[AccountInfo] identifies voter.
 |`need`|optional|
 Map of labels that need to be given to submit. The value is
 currently an empty object.
 |`may`|optional|
 Map of labels that can be used, but do not affect submit.
-AccountInfo identifies voter, if the label has been applied.
+link:rest-api-accounts.html#account-info[AccountInfo] identifies voter,
+if the label has been applied.
 |`impossible`|optional|
 Map of labels that should have been in `need` but cannot be
 used by any user because of access restrictions. The value
diff --git a/Documentation/rest-api-groups.txt b/Documentation/rest-api-groups.txt
index 7ede30f..e331404 100644
--- a/Documentation/rest-api-groups.txt
+++ b/Documentation/rest-api-groups.txt
@@ -107,6 +107,23 @@
 get::/groups/
 ****
 
+[[group-options]]
+Group Options
+^^^^^^^^^^^^^
+Additional fields can be obtained by adding `o` parameters, each option
+requires more lookups and slows down the query response time to the
+client so they are generally disabled by default. Optional fields are:
+
+[[includes]]
+--
+* `INCLUDES`: include list of directly included groups.
+--
+
+[[members]]
+--
+* `MEMBERS`: include list of direct group members.
+--
+
 Check if a group is owned by the calling user
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 By setting the option `owned` and specifying a group to inspect with
@@ -229,6 +246,54 @@
 If the group creation fails because the name is already in use the
 response is "`409 Conflict`".
 
+[[get-group-detail]]
+GET /groups/\{group-id\}/detail (Get Group Detail)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Retrieves a group with the direct link:#members[members] and the
+directly link:#includes[included groups].
+
+.Request
+----
+  GET /groups/6a1e70e1a88782771a91808c8af9bbb7a9871389/detail HTTP/1.0
+----
+
+As response a link:#group-info[GroupInfo] entity is returned that
+describes the group.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json;charset=UTF-8
+
+  )]}'
+  {
+    "kind": "gerritcodereview#group",
+    "id": "6a1e70e1a88782771a91808c8af9bbb7a9871389",
+    "name": "Administrators",
+    "url": "#/admin/groups/uuid-6a1e70e1a88782771a91808c8af9bbb7a9871389",
+    "options": {
+    },
+    "description": "Gerrit Site Administrators",
+    "group_id": 1,
+    "owner": "Administrators",
+    "owner_id": "6a1e70e1a88782771a91808c8af9bbb7a9871389",
+    "members": [
+      {
+        "_account_id": 1000097,
+        "name": "Jane Roe",
+        "email": "jane.roe@example.com"
+      },
+      {
+        "_account_id": 1000096,
+        "name": "John Doe",
+        "email": "john.doe@example.com"
+      }
+    ],
+    "includes": []
+  }
+----
+
 [[get-group-name]]
 GET /groups/\{group-id\}/name (Get Group Name)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -494,9 +559,9 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Lists the direct members of a Gerrit internal group.
 
-As result a list of link:#member-info[MemberInfo] entries is returned.
-The entries in the list are sorted by full name, preferred email and
-id.
+As result a list of detailed link:rest-api-accounts.html#account-info[
+AccountInfo] entries is returned. The entries in the list are sorted by
+full name, preferred email and id.
 
 .Request
 ----
@@ -512,20 +577,14 @@
   )]}'
   [
     {
-      "kind": "gerritcodereview#member",
-      "full_name": "Jane Roe",
-      "id": "1000097",
-      "account_id": 1000097,
-      "preferred_email": "jane.roe@example.com",
-      "user_name": "jane"
+      "_account_id": 1000097,
+      "name": "Jane Roe",
+      "email": "jane.roe@example.com"
     },
     {
-      "kind": "gerritcodereview#member",
-      "full_name": "John Doe",
-      "id": "1000096",
-      "account_id": 1000096,
-      "preferred_email": "john.doe@example.com",
-      "user_name": "doe"
+      "_account_id": 1000096,
+      "name": "John Doe",
+      "email": "john.doe@example.com"
     }
   ]
 ----
@@ -552,28 +611,19 @@
   )]}'
   [
     {
-      "kind": "gerritcodereview#member",
-      "full_name": "Jane Roe",
-      "id": "1000097",
-      "account_id": 1000097,
-      "preferred_email": "jane.roe@example.com",
-      "user_name": "jane"
+      "_account_id": 1000097,
+      "name": "Jane Roe",
+      "email": "jane.roe@example.com"
     },
     {
-      "kind": "gerritcodereview#member",
-      "full_name": "John Doe",
-      "id": "1000096",
-      "account_id": 1000096,
-      "preferred_email": "john.doe@example.com",
-      "user_name": "doe"
+      "_account_id": 1000096,
+      "name": "John Doe",
+      "email": "john.doe@example.com"
     },
     {
-      "kind": "gerritcodereview#member",
-      "full_name": "Richard Roe",
-      "id": "1000098",
-      "account_id": 1000098,
-      "preferred_email": "richard.roe@example.com",
-      "user_name": "richard"
+      "_account_id": 1000098,
+      "name": "Richard Roe",
+      "email": "richard.roe@example.com"
     }
   ]
 ----
@@ -588,8 +638,8 @@
   GET /groups/834ec36dd5e0ed21a2ff5d7e2255da082d63bbd7/members/1000096 HTTP/1.0
 ----
 
-As response a link:#member-info[MemberInfo] entity is returned that
-describes the group member.
+As response a detailed link:rest-api-accounts.html#account-info[
+AccountInfo] entity is returned that describes the group member.
 
 .Response
 ----
@@ -599,12 +649,9 @@
 
   )]}'
   {
-    "kind": "gerritcodereview#member",
-    "full_name": "John Doe",
-    "id": "1000096",
-    "account_id": 1000096,
-    "preferred_email": "john.doe@example.com",
-    "user_name": "doe"
+    "_account_id": 1000096,
+    "name": "John Doe",
+    "email": "john.doe@example.com"
   }
 ----
 
@@ -618,8 +665,8 @@
   PUT /groups/MyProject-Committers/members/John%20Doe HTTP/1.0
 ----
 
-As response a link:#member-info[MemberInfo] entity is returned that
-describes the group member.
+As response a detailed link:rest-api-accounts.html#account-info[
+AccountInfo] entity is returned that describes the group member.
 
 .Response
 ----
@@ -629,12 +676,9 @@
 
   )]}'
   {
-    "kind": "gerritcodereview#member",
-    "full_name": "John Doe",
-    "id": "1000037",
-    "account_id": 1000037,
-    "preferred_email": "john.doe@example.com",
-    "user_name": "jdoe"
+    "_account_id": 1000037,
+    "name": "John Doe",
+    "email": "john.doe@example.com"
   }
 ----
 
@@ -666,9 +710,10 @@
   }
 ----
 
-As response a list of link:#member-info[MemberInfo] entities is
-returned that describes the group members that were specified in the
-link:#member-input[MemberInput]. A link:#member-info[MemberInfo] entity
+As response a list of detailed link:rest-api-accounts.html#account-info[
+AccountInfo] entities is returned that describes the group members that
+were specified in the link:#member-input[MemberInput]. An
+link:rest-api-accounts.html#account-info[AccountInfo] entity
 is returned for each user specified in the input, independently of
 whether the user was newly added to the group or whether the user was
 already a member of the group.
@@ -682,20 +727,14 @@
   )]}'
   [
     {
-      "kind": "gerritcodereview#member",
-      "full_name": "Jane Roe",
-      "id": "1000057",
-      "account_id": 1000057,
-      "preferred_email": "jane.roe@example.com",
-      "user_name": "jroe"
+      "_account_id": 1000057,
+      "name": "Jane Roe",
+      "email": "jane.roe@example.com"
     },
     {
-      "kind": "gerritcodereview#member",
-      "full_name": "John Doe",
-      "id": "1000037",
-      "account_id": 1000037,
-      "preferred_email": "john.doe@example.com",
-      "user_name": "jdoe"
+      "_account_id": 1000037,
+      "name": "John Doe",
+      "email": "john.doe@example.com"
     }
   ]
 ----
@@ -1006,6 +1045,14 @@
 |`group_id`    |only for internal groups|The numeric ID of the group.
 |`owner`       |only for internal groups|The name of the owner group.
 |`owner_id`    |only for internal groups|The URL encoded UUID of the owner group.
+|`members`     |optional, only for internal groups|
+A list of link:rest-api-accounts.html#account-info[AccountInfo]
+entities describing the direct members. +
+Only set if link:#members[members] are requested.
+|`includes`    |optional, only for internal groups|
+A list of link:#group-info[GroupInfo] entities describing the directly
+included groups. +
+Only set if link:#includes[included groups] are requested.
 |===========================
 
 The type of a group can be deduced from the group's UUID:
@@ -1078,25 +1125,6 @@
 Whether the group is visible to all registered users.
 |=============================
 
-[[member-info]]
-MemberInfo
-~~~~~~~~~~
-The `MemberInfo` entity contains information about an account that is
-member of a group.
-
-[options="header",width="50%",cols="1,^1,5"]
-|===============================
-|Field Name        ||Description
-|`kind`            ||`gerritcodereview#member`
-|`id`              ||The URL encoded ID of the account.
-|`account_id`      ||The numeric ID of the account.
-|`full_name`       |optional|
-The full name of the user ("Given-name Surname" style).
-|`preferred_email` |optional|
-The email address the user prefers to be contacted through.
-|`user_name`       ||The user name.
-|===============================
-
 [[member-input]]
 MemberInput
 ~~~~~~~~~~~
diff --git a/ReleaseNotes/ReleaseNotes-2.6.txt b/ReleaseNotes/ReleaseNotes-2.6.txt
index a7e5ee3..249df7f 100644
--- a/ReleaseNotes/ReleaseNotes-2.6.txt
+++ b/ReleaseNotes/ReleaseNotes-2.6.txt
@@ -85,6 +85,9 @@
 * `query` includes author and change size information when given
   certain options on the command line.
 
+* `test-submit-rule` tests the `can_submit` rule with a prolog script loaded from a file or stdin.
+* `ls-user-refs` lists which refs are visible for a given user.
+
 Plugins
 ~~~~~~~
 * Plugins can contribute Prolog facts/predicates from Java.
@@ -138,6 +141,13 @@
   may need to retry a change not yet merged by the first. Please note
   we still do not believe this is sufficient to enable multi-master.
 
+SSH
+~~~
+* `plugin ls` shows status of enabled plugins as "ENABLED".
+* `review --restore` allows a review score to be added on the restored change.
+* link:https://code.google.com/p/gerrit/issues/detail?id=1721[Issue 1721]:
+`review --message` only adds the message once.
+
 Documentation
 -------------
 
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java b/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java
index e3f9236..3d08d06 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java
@@ -46,10 +46,6 @@
     return value;
   }
 
-  public static ListChangesOption fromValue(int value) {
-    return ListChangesOption.values()[value];
-  }
-
   public static EnumSet<ListChangesOption> fromBits(int v) {
     EnumSet<ListChangesOption> r = EnumSet.noneOf(ListChangesOption.class);
     for (ListChangesOption o : ListChangesOption.values()) {
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountDashboardInfo.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountDashboardInfo.java
deleted file mode 100644
index e24900b..0000000
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountDashboardInfo.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.common.data;
-
-import com.google.gerrit.reviewdb.client.Account;
-
-import java.util.List;
-
-/** Summary information needed to display an account dashboard. */
-public class AccountDashboardInfo {
-  protected AccountInfoCache accounts;
-  protected Account.Id owner;
-  protected List<ChangeInfo> byOwner;
-  protected List<ChangeInfo> forReview;
-  protected List<ChangeInfo> closed;
-
-  protected AccountDashboardInfo() {
-  }
-
-  public AccountDashboardInfo(final Account.Id forUser) {
-    owner = forUser;
-  }
-
-  public AccountInfoCache getAccounts() {
-    return accounts;
-  }
-
-  public void setAccounts(final AccountInfoCache ac) {
-    accounts = ac;
-  }
-
-  public Account.Id getOwner() {
-    return owner;
-  }
-
-  public List<ChangeInfo> getByOwner() {
-    return byOwner;
-  }
-
-  public void setByOwner(List<ChangeInfo> c) {
-    byOwner = c;
-  }
-
-  public List<ChangeInfo> getForReview() {
-    return forReview;
-  }
-
-  public void setForReview(List<ChangeInfo> c) {
-    forReview = c;
-  }
-
-  public List<ChangeInfo> getClosed() {
-    return closed;
-  }
-
-  public void setClosed(List<ChangeInfo> c) {
-    closed = c;
-  }
-}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/groups/ListGroupsOption.java b/gerrit-common/src/main/java/com/google/gerrit/common/groups/ListGroupsOption.java
new file mode 100644
index 0000000..2c99ac6
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/groups/ListGroupsOption.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.common.groups;
+
+import java.util.EnumSet;
+
+
+/** Output options available when using {@code /groups/} RPCs. */
+public enum ListGroupsOption {
+  /** Return information on the direct group members. */
+  MEMBERS(0),
+
+  /** Return information on the directly included groups. */
+  INCLUDES(1);
+
+  private final int value;
+
+  private ListGroupsOption(int v) {
+    this.value = v;
+  }
+
+  public int getValue() {
+    return value;
+  }
+
+  public static ListGroupsOption fromValue(int value) {
+    return ListGroupsOption.values()[value];
+  }
+
+  public static EnumSet<ListGroupsOption> fromBits(int v) {
+    EnumSet<ListGroupsOption> r = EnumSet.noneOf(ListGroupsOption.class);
+    for (ListGroupsOption o : ListGroupsOption.values()) {
+      if ((v & (1 << o.value)) != 0) {
+        r.add(o);
+        v &= ~(1 << o.value);
+      }
+      if (v == 0) {
+        return r;
+      }
+    }
+    if (v != 0) {
+      throw new IllegalArgumentException("unknown " + Integer.toHexString(v));
+    }
+    return r;
+  }
+
+  public static int toBits(EnumSet<ListGroupsOption> set) {
+    int r = 0;
+    for (ListGroupsOption o : set) {
+      r |= 1 << o.value;
+    }
+    return r;
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
index 03ab008..f55ddb9 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
@@ -756,7 +756,7 @@
           return;
         }
 
-        GroupApi.getGroup(group, new GerritCallback<GroupInfo>() {
+        GroupApi.getGroupDetail(group, new GerritCallback<GroupInfo>() {
           @Override
           public void onSuccess(GroupInfo group) {
             if (panel == null || panel.isEmpty()) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
index 4a9ae22..bc40097 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.client;
 
-import com.google.gerrit.client.changes.AccountInfo;
+import com.google.gerrit.client.account.AccountInfo;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
 import com.google.gwt.i18n.client.DateTimeFormat;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountInfo.java
similarity index 96%
rename from gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountInfo.java
rename to gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountInfo.java
index b9ee62d..601d807 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountInfo.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.client.changes;
+package com.google.gerrit.client.account;
 
 import com.google.gwt.core.client.JavaScriptObject;
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
index 28a1498..c276a89 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
@@ -17,11 +17,9 @@
 import com.google.gerrit.client.Dispatcher;
 import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.client.VoidResult;
+import com.google.gerrit.client.account.AccountInfo;
 import com.google.gerrit.client.groups.GroupApi;
 import com.google.gerrit.client.groups.GroupInfo;
-import com.google.gerrit.client.groups.GroupList;
-import com.google.gerrit.client.groups.MemberInfo;
-import com.google.gerrit.client.groups.MemberList;
 import com.google.gerrit.client.rpc.GerritCallback;
 import com.google.gerrit.client.rpc.Natives;
 import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
@@ -30,7 +28,6 @@
 import com.google.gerrit.client.ui.FancyFlexTable;
 import com.google.gerrit.client.ui.Hyperlink;
 import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
@@ -153,18 +150,8 @@
   protected void display(final GroupInfo group, final boolean canModify) {
     if (AccountGroup.isInternalGroup(group.getGroupUUID())
         && !AccountGroup.isSystemGroup(group.getGroupUUID())) {
-      MemberList.all(getGroupUUID(), new GerritCallback<MemberList>() {
-        @Override
-        public void onSuccess(MemberList result) {
-          members.display(Natives.asList(result));
-        }
-      });
-      GroupList.included(getGroupUUID(), new GerritCallback<GroupList>() {
-        @Override
-        public void onSuccess(GroupList result) {
-          includes.display(Natives.asList(result));
-        }
-      });
+      members.display(Natives.asList(group.members()));
+      includes.display(Natives.asList(group.includes()));
     } else {
       memberPanel.setVisible(false);
       includePanel.setVisible(false);
@@ -184,8 +171,8 @@
 
     addMemberBox.setEnabled(false);
     GroupApi.addMember(getGroupUUID(), nameEmail,
-        new GerritCallback<MemberInfo>() {
-          public void onSuccess(final MemberInfo memberInfo) {
+        new GerritCallback<AccountInfo>() {
+          public void onSuccess(final AccountInfo memberInfo) {
             addMemberBox.setEnabled(true);
             addMemberBox.setText("");
             members.insert(memberInfo);
@@ -222,7 +209,7 @@
         });
   }
 
-  private class MemberTable extends FancyFlexTable<MemberInfo> {
+  private class MemberTable extends FancyFlexTable<AccountInfo> {
     private boolean enabled = true;
 
     MemberTable() {
@@ -238,7 +225,7 @@
     void setEnabled(final boolean enabled) {
       this.enabled = enabled;
       for (int row = 1; row < table.getRowCount(); row++) {
-        final MemberInfo i = getRowItem(row);
+        final AccountInfo i = getRowItem(row);
         if (i != null) {
           ((CheckBox) table.getWidget(row, 1)).setEnabled(enabled);
         }
@@ -246,11 +233,11 @@
     }
 
     void deleteChecked() {
-      final HashSet<Account.Id> ids = new HashSet<Account.Id>();
+      final HashSet<Integer> ids = new HashSet<Integer>();
       for (int row = 1; row < table.getRowCount(); row++) {
-        final MemberInfo i = getRowItem(row);
+        final AccountInfo i = getRowItem(row);
         if (i != null && ((CheckBox) table.getWidget(row, 1)).getValue()) {
-          ids.add(i.getAccountId());
+          ids.add(i._account_id());
         }
       }
       if (!ids.isEmpty()) {
@@ -258,8 +245,8 @@
             new GerritCallback<VoidResult>() {
               public void onSuccess(final VoidResult result) {
                 for (int row = 1; row < table.getRowCount();) {
-                  final MemberInfo i = getRowItem(row);
-                  if (i != null && ids.contains(i.getAccountId())) {
+                  final AccountInfo i = getRowItem(row);
+                  if (i != null && ids.contains(i._account_id())) {
                     table.removeRow(row);
                   } else {
                     row++;
@@ -270,11 +257,11 @@
       }
     }
 
-    void display(final List<MemberInfo> result) {
+    void display(final List<AccountInfo> result) {
       while (1 < table.getRowCount())
         table.removeRow(table.getRowCount() - 1);
 
-      for (final MemberInfo i : result) {
+      for (final AccountInfo i : result) {
         final int row = table.getRowCount();
         table.insertRow(row);
         applyDataRowStyle(row);
@@ -282,22 +269,21 @@
       }
     }
 
-    void insert(MemberInfo info) {
-      Comparator<MemberInfo> c = new Comparator<MemberInfo>() {
+    void insert(AccountInfo info) {
+      Comparator<AccountInfo> c = new Comparator<AccountInfo>() {
         @Override
-        public int compare(MemberInfo a, MemberInfo b) {
-          int cmp = nullToEmpty(a.fullName()).compareTo(nullToEmpty(b.fullName()));
+        public int compare(AccountInfo a, AccountInfo b) {
+          int cmp = nullToEmpty(a.name()).compareTo(nullToEmpty(b.name()));
           if (cmp != 0) {
             return cmp;
           }
 
-          cmp = nullToEmpty(a.preferredEmail()).compareTo(
-                  nullToEmpty(b.preferredEmail()));
+          cmp = nullToEmpty(a.email()).compareTo(nullToEmpty(b.email()));
           if (cmp != 0) {
             return cmp;
           }
 
-          return a.getAccountId().get() - b.getAccountId().get();
+          return a._account_id() - b._account_id();
         }
 
         public String nullToEmpty(String str) {
@@ -309,7 +295,7 @@
       int right = table.getRowCount() - 1;
       while (left <= right) {
         int middle = (left + right) >>> 1; // (left+right)/2
-        MemberInfo i = getRowItem(middle);
+        AccountInfo i = getRowItem(middle);
         int cmp = c.compare(i, info);
 
         if (cmp < 0) {
@@ -328,12 +314,12 @@
       populate(insertPosition, info);
     }
 
-    void populate(final int row, final MemberInfo i) {
+    void populate(final int row, final AccountInfo i) {
       CheckBox checkBox = new CheckBox();
       table.setWidget(row, 1, checkBox);
       checkBox.setEnabled(enabled);
-      table.setWidget(row, 2, new AccountLink(i.asAccountInfo()));
-      table.setText(row, 3, i.preferredEmail());
+      table.setWidget(row, 2, new AccountLink(i));
+      table.setText(row, 3, i.email());
 
       final FlexCellFormatter fmt = table.getFlexCellFormatter();
       fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().iconCell());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java
index 26b294e..bae5448 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.client.ConfirmationDialog;
 import com.google.gerrit.client.ErrorDialog;
 import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.account.AccountInfo;
 import com.google.gerrit.client.changes.ChangeInfo.ApprovalInfo;
 import com.google.gerrit.client.changes.ChangeInfo.LabelInfo;
 import com.google.gerrit.client.rpc.GerritCallback;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java
index 91b261a..f4453da 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.client.changes;
 
+import com.google.gerrit.client.account.AccountInfo;
 import com.google.gerrit.client.rpc.NativeMap;
 import com.google.gerrit.client.rpc.NativeString;
 import com.google.gerrit.client.rpc.Natives;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
index 80de5da..15b033b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.client.Dispatcher;
 import com.google.gerrit.client.FormatUtil;
 import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.account.AccountInfo;
 import com.google.gerrit.client.rpc.GerritCallback;
 import com.google.gerrit.client.ui.CommentPanel;
 import com.google.gerrit.client.ui.ComplexDisclosurePanel;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java
index 9343b47..0b178f2 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java
@@ -15,10 +15,10 @@
 package com.google.gerrit.client.groups;
 
 import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.rpc.Natives;
+import com.google.gerrit.client.account.AccountInfo;
 import com.google.gerrit.client.rpc.NativeString;
+import com.google.gerrit.client.rpc.Natives;
 import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.core.client.JsArray;
@@ -37,8 +37,8 @@
     new RestApi("/groups/").id(groupName).ifNoneMatch().put(in, cb);
   }
 
-  public static void getGroup(String group, AsyncCallback<GroupInfo> cb) {
-    group(group).get(cb);
+  public static void getGroupDetail(String group, AsyncCallback<GroupInfo> cb) {
+    group(group).view("detail").get(cb);
   }
 
   /** Get the name of a group */
@@ -100,20 +100,20 @@
 
   /** Add member to a group. */
   public static void addMember(AccountGroup.UUID group, String member,
-      AsyncCallback<MemberInfo> cb) {
+      AsyncCallback<AccountInfo> cb) {
     members(group).id(member).put(cb);
   }
 
   /** Add members to a group. */
   public static void addMembers(AccountGroup.UUID group,
       Set<String> members,
-      final AsyncCallback<JsArray<MemberInfo>> cb) {
+      final AsyncCallback<JsArray<AccountInfo>> cb) {
     if (members.size() == 1) {
       addMember(group,
           members.iterator().next(),
-          new AsyncCallback<MemberInfo>() {
+          new AsyncCallback<AccountInfo>() {
             @Override
-            public void onSuccess(MemberInfo result) {
+            public void onSuccess(AccountInfo result) {
               cb.onSuccess(Natives.arrayOf(result));
             }
 
@@ -133,14 +133,13 @@
 
   /** Remove members from a group. */
   public static void removeMembers(AccountGroup.UUID group,
-      Set<Account.Id> ids, final AsyncCallback<VoidResult> cb) {
+      Set<Integer> ids, final AsyncCallback<VoidResult> cb) {
     if (ids.size() == 1) {
-      Account.Id u = ids.iterator().next();
-      members(group).id(u.toString()).delete(cb);
+      members(group).id(ids.iterator().next().toString()).delete(cb);
     } else {
       MemberInput in = MemberInput.create();
-      for (Account.Id u : ids) {
-        in.add_member(u.toString());
+      for (Integer id : ids) {
+        in.add_member(id.toString());
       }
       group(group).view("members.delete").post(in, cb);
     }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupInfo.java
index 39b6b73..3b4abe5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupInfo.java
@@ -14,8 +14,10 @@
 
 package com.google.gerrit.client.groups;
 
+import com.google.gerrit.client.account.AccountInfo;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
 import com.google.gwt.http.client.URL;
 
 public class GroupInfo extends JavaScriptObject {
@@ -34,6 +36,8 @@
   public final native String url() /*-{ return this.url; }-*/;
   public final native String owner() /*-{ return this.owner; }-*/;
   public final native void owner(String o) /*-{ if(o)this.owner=o; }-*/;
+  public final native JsArray<AccountInfo> members() /*-{ return this.members; }-*/;
+  public final native JsArray<GroupInfo> includes() /*-{ return this.includes; }-*/;
 
   private final native int group_id() /*-{ return this.group_id; }-*/;
   private final native String owner_id() /*-{ return this.owner_id; }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/MemberInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/MemberInfo.java
deleted file mode 100644
index f7369c3..0000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/MemberInfo.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.client.groups;
-
-import com.google.gerrit.client.changes.AccountInfo;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class MemberInfo extends JavaScriptObject {
-  public final Account.Id getAccountId() {
-    return new Account.Id(account_id());
-  }
-
-  public final AccountInfo asAccountInfo() {
-    return AccountInfo.create(account_id(), fullName(), preferredEmail());
-  }
-
-  private final native int account_id() /*-{ return this.account_id || 0; }-*/;
-  public final native String fullName() /*-{ return this.full_name; }-*/;
-  public final native String preferredEmail() /*-{ return this.preferred_email; }-*/;
-
-  protected MemberInfo() {
-  }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/MemberList.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/MemberList.java
index 47c776d..d788a1d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/MemberList.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/MemberList.java
@@ -14,12 +14,13 @@
 
 package com.google.gerrit.client.groups;
 
+import com.google.gerrit.client.account.AccountInfo;
 import com.google.gerrit.client.rpc.RestApi;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gwt.core.client.JsArray;
 import com.google.gwt.user.client.rpc.AsyncCallback;
 
-public class MemberList extends JsArray<MemberInfo> {
+public class MemberList extends JsArray<AccountInfo> {
   public static void all(AccountGroup.UUID group,
       AsyncCallback<MemberList> callback) {
     new RestApi("/groups/").id(group.get()).view("members").get(callback);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
index 90c19fe..2f8d135 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
@@ -17,7 +17,7 @@
 import com.google.gerrit.client.Dispatcher;
 import com.google.gerrit.client.FormatUtil;
 import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.AccountInfo;
+import com.google.gerrit.client.account.AccountInfo;
 import com.google.gerrit.client.changes.PatchTable;
 import com.google.gerrit.client.changes.Util;
 import com.google.gerrit.client.rpc.GerritCallback;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLink.java
index c3f56bc..fd52777 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLink.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLink.java
@@ -16,7 +16,7 @@
 
 import com.google.gerrit.client.FormatUtil;
 import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.AccountInfo;
+import com.google.gerrit.client.account.AccountInfo;
 import com.google.gerrit.common.PageLinks;
 import com.google.gerrit.common.data.AccountInfoCache;
 import com.google.gerrit.reviewdb.client.Account;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentPanel.java
index 4e918aa..8f8c7ea 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentPanel.java
@@ -16,7 +16,7 @@
 
 import com.google.gerrit.client.FormatUtil;
 import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.AccountInfo;
+import com.google.gerrit.client.account.AccountInfo;
 import com.google.gwt.event.dom.client.BlurEvent;
 import com.google.gwt.event.dom.client.BlurHandler;
 import com.google.gwt.event.dom.client.ClickEvent;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
index 3341187..7127dce 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
@@ -333,8 +333,8 @@
       MissingObjectException, IncorrectObjectTypeException, IOException,
       InvalidChangeOperationException, PatchSetInfoNotAvailableException {
     final Change.Id changeId = patchSetId.getParentKey();
-    final PatchSet patch = db.patchSets().get(patchSetId);
-    if (patch == null) {
+    final PatchSet originalPS = db.patchSets().get(patchSetId);
+    if (originalPS == null) {
       throw new NoSuchChangeException(changeId);
     }
 
@@ -345,7 +345,7 @@
     final RevWalk revWalk = new RevWalk(git);
     try {
       RevCommit commit =
-          revWalk.parseCommit(ObjectId.fromString(patch.getRevision().get()));
+          revWalk.parseCommit(ObjectId.fromString(originalPS.getRevision().get()));
       if (commit.getFullMessage().equals(message)) {
         throw new InvalidChangeOperationException("New commit message cannot be same as existing commit message");
       }
@@ -372,7 +372,6 @@
         oi.release();
       }
 
-      final PatchSet originalPS = db.patchSets().get(patchSetId);
       PatchSet.Id id = nextPatchSetId(git, change.currentPatchSetId());
       final PatchSet newPatchSet = new PatchSet(id);
       newPatchSet.setCreatedOn(new Timestamp(now.getTime()));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/AccountInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountInfo.java
similarity index 82%
rename from gerrit-server/src/main/java/com/google/gerrit/server/change/AccountInfo.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/account/AccountInfo.java
index 437947c2..a296716 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/AccountInfo.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountInfo.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.account;
 
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Iterables;
@@ -21,8 +21,6 @@
 import com.google.common.collect.Multimap;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.account.AccountState;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -33,8 +31,8 @@
 import java.util.Map;
 
 public class AccountInfo {
-  static class Loader {
-    interface Factory {
+  public static class Loader {
+    public interface Factory {
       Loader create(boolean detailed);
     }
 
@@ -76,7 +74,7 @@
       for (AccountInfo info : Iterables.concat(created.values(), provided)) {
         AccountState state = accountCache.getIfPresent(info._id);
         if (state != null) {
-          fill(info, state.getAccount());
+          info.fill(state.getAccount(), detailed);
         } else {
           missing.put(info._id, info);
         }
@@ -84,7 +82,7 @@
       if (!missing.isEmpty()) {
         for (Account account : db.get().accounts().get(missing.keySet())) {
           for (AccountInfo info : missing.get(account.getId())) {
-            fill(info, account);
+            info.fill(account, detailed);
           }
         }
       }
@@ -97,23 +95,29 @@
       }
       fill();
     }
-
-    private void fill(AccountInfo info, Account account) {
-      info.name = account.getFullName();
-      if (detailed) {
-        info._account_id = account.getId().get();
-        info.email = account.getPreferredEmail();
-      }
-    }
   }
 
-  transient Account.Id _id;
+  public static AccountInfo parse(Account a, boolean detailed) {
+    AccountInfo ai = new AccountInfo(a.getId());
+    ai.fill(a, detailed);
+    return ai;
+  }
 
-  protected AccountInfo(Account.Id id) {
+  public transient Account.Id _id;
+
+  public AccountInfo(Account.Id id) {
     _id = id;
   }
 
   public Integer _account_id;
   public String name;
   public String email;
+
+  private void fill(Account account, boolean detailed) {
+    name = account.getFullName();
+    if (detailed) {
+      _account_id = account.getId().get();
+      email = account.getPreferredEmail();
+    }
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetGroups.java
index 3b0162b..d270656 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetGroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetGroups.java
@@ -16,12 +16,14 @@
 
 import com.google.common.collect.Lists;
 import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.group.GroupJson;
 import com.google.gerrit.server.group.GroupJson.GroupInfo;
+import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
 import java.util.List;
@@ -37,7 +39,8 @@
   }
 
   @Override
-  public List<GroupInfo> apply(AccountResource resource) {
+  public List<GroupInfo> apply(AccountResource resource)
+      throws ResourceNotFoundException, OrmException {
     IdentifiedUser user = resource.getUser();
     Account.Id userId = user.getAccountId();
     List<GroupInfo> groups = Lists.newArrayList();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index 1a5d9bb..8656864 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -56,6 +56,7 @@
 import com.google.gerrit.server.AnonymousUser;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountInfo;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.patch.PatchList;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
index 1bf2542..7e175a7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
@@ -22,6 +22,7 @@
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.RestApiModule;
+import com.google.gerrit.server.account.AccountInfo;
 import com.google.gerrit.server.change.Reviewed.DeleteReviewed;
 import com.google.gerrit.server.change.Reviewed.PutReviewed;
 import com.google.gerrit.server.config.FactoryModule;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
index 6c8f732..fcf2bc6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
@@ -39,6 +39,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountInfo;
 import com.google.gerrit.server.account.GroupMembers;
 import com.google.gerrit.server.change.PostReviewers.Input;
 import com.google.gerrit.server.change.ReviewerJson.PostResult;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
index 76f8c0b..c3c951d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
@@ -27,6 +27,7 @@
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.account.AccountInfo;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.workflow.CategoryFunction;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java
index fdf63be..170f8e8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java
@@ -29,6 +29,7 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.rules.RulesCache;
+import com.google.gerrit.server.account.AccountInfo;
 import com.google.gerrit.server.change.TestSubmitRule.Input;
 import com.google.gerrit.server.project.RuleEvalException;
 import com.google.gerrit.server.project.SubmitRuleEvaluator;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java
index be68585..94791cb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java
@@ -82,8 +82,8 @@
 
   @Override
   public List<GroupInfo> apply(GroupResource resource, Input input)
-      throws MethodNotAllowedException, AuthException, BadRequestException,
-      OrmException {
+      throws ResourceNotFoundException, MethodNotAllowedException,
+      AuthException, BadRequestException, OrmException {
     AccountGroup group = resource.toAccountGroup();
     if (group == null) {
       throw new MethodNotAllowedException();
@@ -153,8 +153,8 @@
 
     @Override
     public GroupInfo apply(GroupResource resource, Input input)
-        throws MethodNotAllowedException, AuthException, BadRequestException,
-        OrmException {
+        throws ResourceNotFoundException, MethodNotAllowedException,
+        AuthException, BadRequestException, OrmException {
       AddIncludedGroups.Input in = new AddIncludedGroups.Input();
       in.groups = ImmutableList.of(id);
       List<GroupInfo> list = put.get().apply(resource, in);
@@ -177,7 +177,9 @@
     }
 
     @Override
-    public Object apply(IncludedGroupResource resource, PutIncludedGroup.Input input) {
+    public Object apply(IncludedGroupResource resource,
+        PutIncludedGroup.Input input) throws ResourceNotFoundException,
+        OrmException {
       // Do nothing, the group is already included.
       return get.get().apply(resource);
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
index 7b540bb..5233e11 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
@@ -35,6 +35,7 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountException;
+import com.google.gerrit.server.account.AccountInfo;
 import com.google.gerrit.server.account.AccountManager;
 import com.google.gerrit.server.account.AccountResolver;
 import com.google.gerrit.server.account.AccountsCollection;
@@ -42,7 +43,6 @@
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.server.group.AddMembers.Input;
-import com.google.gerrit.server.group.MembersCollection.MemberInfo;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -94,7 +94,7 @@
   }
 
   @Override
-  public List<MemberInfo> apply(GroupResource resource, Input input)
+  public List<AccountInfo> apply(GroupResource resource, Input input)
       throws AuthException, MethodNotAllowedException, BadRequestException,
       OrmException {
     AccountGroup internalGroup = resource.toAccountGroup();
@@ -107,7 +107,7 @@
     Map<Account.Id, AccountGroupMember> newAccountGroupMembers = Maps.newHashMap();
     List<AccountGroupMemberAudit> newAccountGroupMemberAudits = Lists.newLinkedList();
     BadRequestHandler badRequest = new BadRequestHandler("adding new group members");
-    List<MemberInfo> result = Lists.newLinkedList();
+    List<AccountInfo> result = Lists.newLinkedList();
     Account.Id me = ((IdentifiedUser) control.getCurrentUser()).getAccountId();
 
     for (String nameOrEmail : input.members) {
@@ -136,7 +136,7 @@
           newAccountGroupMemberAudits.add(new AccountGroupMemberAudit(m, me));
         }
       }
-      result.add(MembersCollection.parse(a));
+      result.add(AccountInfo.parse(a, true));
     }
 
     badRequest.failOnError();
@@ -205,7 +205,7 @@
         OrmException {
       AddMembers.Input in = new AddMembers.Input();
       in._oneMember = id;
-      List<MemberInfo> list = put.get().apply(resource, in);
+      List<AccountInfo> list = put.get().apply(resource, in);
       if (list.size() == 1) {
         return list.get(0);
       }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java
index cb0df21..f05694d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java
@@ -78,8 +78,8 @@
 
   @Override
   public GroupInfo apply(TopLevelResource resource, Input input)
-      throws AuthException, BadRequestException, OrmException,
-      NameAlreadyUsedException {
+      throws ResourceNotFoundException, AuthException, BadRequestException,
+      OrmException, NameAlreadyUsedException {
     if (input == null) {
       input = new Input();
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetDetail.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetDetail.java
new file mode 100644
index 0000000..adb04cd
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetDetail.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.group;
+
+import com.google.gerrit.common.groups.ListGroupsOption;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.group.GroupJson.GroupInfo;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+
+public class GetDetail implements RestReadView<GroupResource> {
+  private final GroupJson json;
+
+  @Inject
+  GetDetail(GroupJson json) {
+    this.json = json.addOption(ListGroupsOption.MEMBERS)
+        .addOption(ListGroupsOption.INCLUDES);
+  }
+
+  @Override
+  public GroupInfo apply(GroupResource rsrc) throws ResourceNotFoundException,
+      OrmException {
+    return json.format(rsrc);
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetGroup.java
index 0dc9a3f..32cf9be 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetGroup.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetGroup.java
@@ -14,8 +14,10 @@
 
 package com.google.gerrit.server.group;
 
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.group.GroupJson.GroupInfo;
+import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
 class GetGroup implements RestReadView<GroupResource> {
@@ -27,7 +29,8 @@
   }
 
   @Override
-  public GroupInfo apply(GroupResource resource) {
+  public GroupInfo apply(GroupResource resource)
+      throws ResourceNotFoundException, OrmException {
     return json.format(resource.getGroup());
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetIncludedGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetIncludedGroup.java
index d750cfc..7e63816 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetIncludedGroup.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetIncludedGroup.java
@@ -14,8 +14,10 @@
 
 package com.google.gerrit.server.group;
 
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.group.GroupJson.GroupInfo;
+import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
 public class GetIncludedGroup implements RestReadView<IncludedGroupResource>  {
@@ -27,7 +29,8 @@
   }
 
   @Override
-  public GroupInfo apply(IncludedGroupResource rsrc) {
+  public GroupInfo apply(IncludedGroupResource rsrc)
+      throws ResourceNotFoundException, OrmException {
     return json.format(rsrc.getMemberDescription());
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetMember.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetMember.java
index feaa09b..5651001 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetMember.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetMember.java
@@ -15,11 +15,11 @@
 package com.google.gerrit.server.group;
 
 import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.server.group.MembersCollection.MemberInfo;
+import com.google.gerrit.server.account.AccountInfo;
 
 public class GetMember implements RestReadView<MemberResource> {
   @Override
-  public MemberInfo apply(MemberResource resource) {
-    return MembersCollection.parse(resource.getMember().getAccount());
+  public AccountInfo apply(MemberResource resource) {
+    return AccountInfo.parse(resource.getMember().getAccount(), true);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOwner.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOwner.java
index 3aab424..797544d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOwner.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOwner.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.group.GroupJson.GroupInfo;
+import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
 public class GetOwner implements RestReadView<GroupResource> {
@@ -34,7 +35,8 @@
   }
 
   @Override
-  public GroupInfo apply(GroupResource resource) throws ResourceNotFoundException {
+  public GroupInfo apply(GroupResource resource)
+      throws ResourceNotFoundException, OrmException {
     AccountGroup group = resource.toAccountGroup();
     if (group == null) {
       throw new ResourceNotFoundException();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupJson.java
index 39fd493..6a729fc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupJson.java
@@ -14,23 +14,75 @@
 
 package com.google.gerrit.server.group;
 
+import static com.google.gerrit.common.groups.ListGroupsOption.INCLUDES;
+import static com.google.gerrit.common.groups.ListGroupsOption.MEMBERS;
+
 import com.google.common.base.Strings;
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.common.data.GroupDescriptions;
+import com.google.gerrit.common.groups.ListGroupsOption;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.Url;
 import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.account.AccountInfo;
 import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupControl;
+import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.List;
 
 public class GroupJson {
   private final GroupCache groupCache;
+  private final GroupControl.Factory groupControlFactory;
+  private final Provider<ListMembers> listMembers;
+  private final Provider<ListIncludedGroups> listIncludes;
+  private EnumSet<ListGroupsOption> options;
 
   @Inject
-  GroupJson(GroupCache groupCache) {
+  GroupJson(GroupCache groupCache, GroupControl.Factory groupControlFactory,
+      Provider<ListMembers> listMembers,
+      Provider<ListIncludedGroups> listIncludes) {
     this.groupCache = groupCache;
+    this.groupControlFactory = groupControlFactory;
+    this.listMembers = listMembers;
+    this.listIncludes = listIncludes;
+
+    options = EnumSet.noneOf(ListGroupsOption.class);
   }
 
-  public GroupInfo format(GroupDescription.Basic group) {
+  public GroupJson addOption(ListGroupsOption o) {
+    options.add(o);
+    return this;
+  }
+
+  public GroupJson addOptions(Collection<ListGroupsOption> o) {
+    options.addAll(o);
+    return this;
+  }
+
+  public GroupInfo format(GroupResource rsrc) throws ResourceNotFoundException,
+      OrmException {
+    GroupInfo info = init(rsrc.getGroup());
+    initMembersAndIncludes(rsrc, info);
+    return info;
+  }
+
+  public GroupInfo format(GroupDescription.Basic group)
+      throws ResourceNotFoundException, OrmException {
+    GroupInfo info = init(group);
+    if (options.contains(MEMBERS) || options.contains(INCLUDES)) {
+      GroupResource rsrc =
+          new GroupResource(groupControlFactory.controlFor(group));
+      initMembersAndIncludes(rsrc, info);
+    }
+    return info;
+  }
+
+  private GroupInfo init(GroupDescription.Basic group) {
     GroupInfo info = new GroupInfo();
     info.id = Url.encode(group.getGroupUUID().get());
     info.name = Strings.emptyToNull(group.getName());
@@ -53,6 +105,18 @@
     return info;
   }
 
+  private GroupInfo initMembersAndIncludes(GroupResource rsrc, GroupInfo info)
+      throws ResourceNotFoundException, OrmException {
+    if (options.contains(MEMBERS)) {
+      info.members = listMembers.get().apply(rsrc);
+    }
+
+    if (options.contains(INCLUDES)) {
+      info.includes = listIncludes.get().apply(rsrc);
+    }
+    return info;
+  }
+
   public static class GroupInfo {
     final String kind = "gerritcodereview#group";
     public String id;
@@ -65,5 +129,9 @@
     public Integer groupId;
     public String owner;
     public String ownerId;
+
+    // These fields are only supplied for internal groups, but only if requested
+    public List<AccountInfo> members;
+    public List<GroupInfo> includes;
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
index 0619a22..88a7ca8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
@@ -22,9 +22,11 @@
 import com.google.gerrit.common.data.GroupDescriptions;
 import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.common.groups.ListGroupsOption;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.extensions.restapi.Url;
@@ -40,6 +42,7 @@
 import com.google.gerrit.server.group.GroupJson.GroupInfo;
 import com.google.gerrit.server.project.ProjectControl;
 import com.google.gson.reflect.TypeToken;
+import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
@@ -47,6 +50,7 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -63,6 +67,7 @@
   private final IdentifiedUser.GenericFactory userFactory;
   private final Provider<GetGroups> accountGetGroups;
   private final GroupJson json;
+  private EnumSet<ListGroupsOption> options;
 
   @Option(name = "--project", aliases = {"-p"},
       usage = "projects for which the groups should be listed")
@@ -92,6 +97,16 @@
   @Option(name = "-m", metaVar = "MATCH", usage = "match group substring")
   private String matchSubstring;
 
+  @Option(name = "-o", multiValued = true, usage = "Output options per group")
+  public void addOption(ListGroupsOption o) {
+    options.add(o);
+  }
+
+  @Option(name = "-O", usage = "Output option flags, in hex")
+  void setOptionFlagsHex(String hex) {
+    options.addAll(ListGroupsOption.fromBits(Integer.parseInt(hex, 16)));
+  }
+
   @Inject
   protected ListGroups(final GroupCache groupCache,
       final GroupControl.Factory groupControlFactory,
@@ -106,6 +121,7 @@
     this.userFactory = userFactory;
     this.accountGetGroups = accountGetGroups;
     this.json = json;
+    this.options = EnumSet.noneOf(ListGroupsOption.class);
   }
 
   public Account.Id getUser() {
@@ -130,7 +146,7 @@
         new TypeToken<Map<String, GroupInfo>>() {}.getType());
   }
 
-  public List<GroupInfo> get() throws NoSuchGroupException {
+  public List<GroupInfo> get() throws ResourceNotFoundException, OrmException {
     List<GroupInfo> groupInfos;
     if (user != null) {
       if (owned) {
@@ -151,7 +167,7 @@
             for (final GroupReference groupRef : groupsRefs) {
               final AccountGroup group = groupCache.get(groupRef.getUUID());
               if (group == null) {
-                throw new NoSuchGroupException(groupRef.getUUID());
+                throw new ResourceNotFoundException(groupRef.getUUID().get());
               }
               groups.put(group.getGroupUUID(), group);
             }
@@ -162,21 +178,23 @@
         }
         groupInfos = Lists.newArrayListWithCapacity(groupList.size());
         for (AccountGroup group : groupList) {
-          groupInfos.add(json.format(GroupDescriptions.forAccountGroup(group)));
+          groupInfos.add(json.addOptions(options).format(
+              GroupDescriptions.forAccountGroup(group)));
         }
       }
     }
     return groupInfos;
   }
 
-  private List<GroupInfo> getGroupsOwnedBy(IdentifiedUser user) {
+  private List<GroupInfo> getGroupsOwnedBy(IdentifiedUser user)
+      throws ResourceNotFoundException, OrmException {
     List<GroupInfo> groups = Lists.newArrayList();
     for (AccountGroup g : filterGroups(groupCache.all())) {
       GroupControl ctl = groupControlFactory.controlFor(g);
       try {
         if (genericGroupControlFactory.controlFor(user, g.getGroupUUID())
             .isOwner()) {
-          groups.add(json.format(ctl.getGroup()));
+          groups.add(json.addOptions(options).format(ctl.getGroup()));
         }
       } catch (NoSuchGroupException e) {
         continue;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListMembers.java
index c09cc4d..f586d55 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListMembers.java
@@ -20,19 +20,15 @@
 import com.google.common.collect.Ordering;
 import com.google.gerrit.common.data.GroupDetail;
 import com.google.gerrit.common.errors.NoSuchGroupException;
-import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuid;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountInfo;
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.account.GroupDetailFactory;
-import com.google.gerrit.server.group.MembersCollection.MemberInfo;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
@@ -47,49 +43,52 @@
 public class ListMembers implements RestReadView<GroupResource> {
   private final GroupCache groupCache;
   private final GroupDetailFactory.Factory groupDetailFactory;
-  private final AccountCache accountCache;
+  private final AccountInfo.Loader accountLoader;
 
   @Option(name = "--recursive", usage = "to resolve included groups recursively")
   private boolean recursive;
 
   @Inject
-  ListMembers(final GroupCache groupCache,
-      final GroupDetailFactory.Factory groupDetailFactory,
-      final AccountCache accountCache) {
+  ListMembers(GroupCache groupCache,
+      GroupDetailFactory.Factory groupDetailFactory,
+      AccountInfo.Loader.Factory accountLoaderFactory) {
     this.groupCache = groupCache;
     this.groupDetailFactory = groupDetailFactory;
-    this.accountCache = accountCache;
+    this.accountLoader = accountLoaderFactory.create(true);
   }
 
   @Override
-  public List<MemberInfo> apply(final GroupResource resource)
-      throws AuthException, BadRequestException, ResourceConflictException,
-      ResourceNotFoundException, Exception {
+  public List<AccountInfo> apply(final GroupResource resource)
+      throws ResourceNotFoundException, OrmException {
     if (resource.toAccountGroup() == null) {
       throw new ResourceNotFoundException(resource.getGroupUUID().get());
     }
-    final Map<Account.Id, MemberInfo> members =
-        getMembers(resource.getGroupUUID(), new HashSet<AccountGroup.UUID>());
-    final List<MemberInfo> memberInfos = Lists.newArrayList(members.values());
-    Collections.sort(memberInfos, new Comparator<MemberInfo>() {
-      @Override
-      public int compare(MemberInfo a, MemberInfo b) {
-        return ComparisonChain.start()
-            .compare(a.fullName, b.fullName, Ordering.natural().nullsFirst())
-            .compare(a.preferredEmail, b.preferredEmail, Ordering.natural().nullsFirst())
-            .compare(a.id, b.id, Ordering.natural().nullsFirst()).result();
-      }
-    });
-    return memberInfos;
+    try {
+      final Map<Account.Id, AccountInfo> members =
+          getMembers(resource.getGroupUUID(), new HashSet<AccountGroup.UUID>());
+      final List<AccountInfo> memberInfos = Lists.newArrayList(members.values());
+      Collections.sort(memberInfos, new Comparator<AccountInfo>() {
+        @Override
+        public int compare(AccountInfo a, AccountInfo b) {
+          return ComparisonChain.start()
+              .compare(a.name, b.name, Ordering.natural().nullsFirst())
+              .compare(a.email, b.email, Ordering.natural().nullsFirst())
+              .compare(a._account_id, b._account_id, Ordering.natural().nullsFirst()).result();
+        }
+      });
+      return memberInfos;
+    } catch (NoSuchGroupException e) {
+      throw new ResourceNotFoundException(resource.getGroupUUID().get());
+    }
   }
 
-  private Map<Account.Id, MemberInfo> getMembers(
+  private Map<Account.Id, AccountInfo> getMembers(
       final AccountGroup.UUID groupUUID,
       final HashSet<AccountGroup.UUID> seenGroups) throws OrmException,
       NoSuchGroupException {
     seenGroups.add(groupUUID);
 
-    final Map<Account.Id, MemberInfo> members = Maps.newHashMap();
+    final Map<Account.Id, AccountInfo> members = Maps.newHashMap();
     final AccountGroup group = groupCache.get(groupUUID);
     if (group == null) {
       // the included group is an external group and can't be resolved
@@ -102,9 +101,7 @@
     if (groupDetail.members != null) {
       for (final AccountGroupMember m : groupDetail.members) {
         if (!members.containsKey(m.getAccountId())) {
-          final Account account =
-              accountCache.get(m.getAccountId()).getAccount();
-          members.put(account.getId(), MembersCollection.parse(account));
+          members.put(m.getAccountId(), accountLoader.get(m.getAccountId()));
         }
       }
     }
@@ -118,6 +115,7 @@
         }
       }
     }
+    accountLoader.fill();
     return members;
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/MembersCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/MembersCollection.java
index 5bd168a..59f9d90 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/MembersCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/MembersCollection.java
@@ -21,7 +21,6 @@
 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.extensions.restapi.Url;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
 import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -86,28 +85,4 @@
   public DynamicMap<RestView<MemberResource>> views() {
     return views;
   }
-
-  public static MemberInfo parse(Account account) {
-    MemberInfo accountInfo = new MemberInfo();
-    accountInfo.setId(account.getId());
-    accountInfo.fullName = account.getFullName();
-    accountInfo.preferredEmail = account.getPreferredEmail();
-    accountInfo.userName = account.getUserName();
-    return accountInfo;
-  }
-
-  static class MemberInfo {
-    final String kind = "gerritcodereview#member";
-
-    String fullName;
-    String id;
-    int accountId;
-    String preferredEmail;
-    String userName;
-
-    void setId(Account.Id i) {
-      accountId = i.get();
-      id = Url.encode(Integer.toString(accountId));
-    }
-  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java
index 74a49e6..97338f1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java
@@ -37,6 +37,7 @@
 
     get(GROUP_KIND).to(GetGroup.class);
     put(GROUP_KIND).to(PutGroup.class);
+    get(GROUP_KIND, "detail").to(GetDetail.class);
     post(GROUP_KIND, "members").to(AddMembers.class);
     post(GROUP_KIND, "members.add").to(AddMembers.class);
     post(GROUP_KIND, "members.delete").to(DeleteMembers.class);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
index 7376b10..c04636c 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
@@ -16,7 +16,7 @@
 
 import com.google.common.base.Objects;
 import com.google.common.base.Strings;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.Url;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.IdentifiedUser;
@@ -29,6 +29,7 @@
 import com.google.gerrit.server.ioutil.ColumnFormatter;
 import com.google.gerrit.sshd.BaseCommand;
 import com.google.gerrit.sshd.CommandMetaData;
+import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
@@ -80,7 +81,7 @@
           identifiedUser, userFactory, accountGetGroups, json);
     }
 
-    void display(final PrintWriter out) throws NoSuchGroupException {
+    void display(final PrintWriter out) throws ResourceNotFoundException, OrmException {
       final ColumnFormatter formatter = new ColumnFormatter(out, '\t');
       for (final GroupInfo info : get()) {
         formatter.addColumn(Objects.firstNonNull(info.name, "n/a"));