Allow group includes to be by UUID instead of group ID

Per discussion on-list, the behavior change in 2.5 to only
allow groups by ID is considered a regression for groups
that want to include external groups (like LDAP).

In order to permit smooth migration, we want to allow both
ID and UUIDs to co-exist for the time being.

Change-Id: I0dbdb15b9c62f2dbce64acbc34c515c7b8229c04
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
index 5cb7fa2..452e2cd 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
@@ -17,7 +17,7 @@
 import com.google.gerrit.common.audit.Audit;
 import com.google.gerrit.common.auth.SignInRequired;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupInclude;
+import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuid;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
 import com.google.gwtjsonrpc.common.AsyncCallback;
 import com.google.gwtjsonrpc.common.RemoteJsonService;
@@ -74,8 +74,8 @@
 
   @Audit
   @SignInRequired
-  void addGroupInclude(AccountGroup.Id groupId, String groupName,
-      AsyncCallback<GroupDetail> callback);
+  void addGroupInclude(AccountGroup.Id groupId, AccountGroup.UUID incGroupUUID,
+      String incGroupName, AsyncCallback<GroupDetail> callback);
 
   @Audit
   @SignInRequired
@@ -85,5 +85,5 @@
   @Audit
   @SignInRequired
   void deleteGroupIncludes(AccountGroup.Id groupId,
-      Set<AccountGroupInclude.Key> keys, AsyncCallback<VoidResult> callback);
+      Set<AccountGroupIncludeByUuid.Key> keys, AsyncCallback<VoidResult> callback);
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java
index 01c7985..24e3b08 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.common.data;
 
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupInclude;
+import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuid;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
 
 import java.util.List;
@@ -25,7 +25,7 @@
   public GroupInfoCache groups;
   public AccountGroup group;
   public List<AccountGroupMember> members;
-  public List<AccountGroupInclude> includes;
+  public List<AccountGroupIncludeByUuid> includes;
   public GroupReference ownerGroup;
   public boolean canModify;
 
@@ -48,7 +48,7 @@
     members = m;
   }
 
-  public void setIncludes(List<AccountGroupInclude> i) {
+  public void setIncludes(List<AccountGroupIncludeByUuid> i) {
     includes = i;
   }
 
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupInfo.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupInfo.java
index 547a5f4..f01365b 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupInfo.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupInfo.java
@@ -18,7 +18,7 @@
 
 /** Summary information about an {@link AccountGroup}, for simple tabular displays. */
 public class GroupInfo {
-  protected AccountGroup.Id id;
+  protected AccountGroup.UUID uuid;
   protected String name;
   protected String description;
 
@@ -32,8 +32,8 @@
    * lookup has failed and a stale group id has been discovered in the data
    * store.
    */
-  public GroupInfo(final AccountGroup.Id id) {
-    this.id = id;
+  public GroupInfo(final AccountGroup.UUID uuid) {
+    this.uuid = uuid;
   }
 
   /**
@@ -42,14 +42,14 @@
    * @param a the data store record holding the specific group details.
    */
   public GroupInfo(final AccountGroup a) {
-    id = a.getId();
+    uuid = a.getGroupUUID();
     name = a.getName();
     description = a.getDescription();
   }
 
   /** @return the unique local id of the group */
-  public AccountGroup.Id getId() {
-    return id;
+  public AccountGroup.UUID getId() {
+    return uuid;
   }
 
   /** @return the name of the group; null if not supplied */
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupInfoCache.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupInfoCache.java
index 6a5dd5c..085973c 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupInfoCache.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupInfoCache.java
@@ -33,13 +33,13 @@
     return EMPTY;
   }
 
-  protected Map<AccountGroup.Id, GroupInfo> groups;
+  protected Map<AccountGroup.UUID, GroupInfo> groups;
 
   protected GroupInfoCache() {
   }
 
   public GroupInfoCache(final Iterable<GroupInfo> list) {
-    groups = new HashMap<AccountGroup.Id, GroupInfo>();
+    groups = new HashMap<AccountGroup.UUID, GroupInfo>();
     for (final GroupInfo gi : list) {
       groups.put(gi.getId(), gi);
     }
@@ -58,15 +58,15 @@
    * @param id the id desired.
    * @return info block for the group.
    */
-  public GroupInfo get(final AccountGroup.Id id) {
-    if (id == null) {
+  public GroupInfo get(final AccountGroup.UUID uuid) {
+    if (uuid == null) {
       return null;
     }
 
-    GroupInfo r = groups.get(id);
+    GroupInfo r = groups.get(uuid);
     if (r == null) {
-      r = new GroupInfo(id);
-      groups.put(id, r);
+      r = new GroupInfo(uuid);
+      groups.put(uuid, r);
     }
     return r;
   }
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 4c0b1ba..d6ab9b6 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
@@ -25,11 +25,10 @@
 import com.google.gerrit.client.ui.SmallHeading;
 import com.google.gerrit.common.data.AccountInfoCache;
 import com.google.gerrit.common.data.GroupDetail;
-import com.google.gerrit.common.data.GroupInfo;
 import com.google.gerrit.common.data.GroupInfoCache;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupInclude;
+import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuid;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
@@ -110,14 +109,15 @@
   }
 
   private void initIncludeList() {
+    final AccountGroupSuggestOracle oracle = new AccountGroupSuggestOracle();
     addIncludeBox =
       new AddMemberBox(Util.C.buttonAddIncludedGroup(),
-          Util.C.defaultAccountGroupName(), new AccountGroupSuggestOracle());
+          Util.C.defaultAccountGroupName(), oracle);
 
     addIncludeBox.addClickHandler(new ClickHandler() {
       @Override
       public void onClick(final ClickEvent event) {
-        doAddNewInclude();
+        doAddNewInclude(oracle);
       }
     });
 
@@ -194,14 +194,14 @@
         });
   }
 
-  void doAddNewInclude() {
+  void doAddNewInclude(final AccountGroupSuggestOracle oracle) {
     final String groupName = addIncludeBox.getText();
     if (groupName.length() == 0) {
       return;
     }
 
     addIncludeBox.setEnabled(false);
-    Util.GROUP_SVC.addGroupInclude(getGroupId(), groupName,
+    Util.GROUP_SVC.addGroupInclude(getGroupId(), oracle.getUUID(groupName), groupName,
         new GerritCallback<GroupDetail>() {
           public void onSuccess(final GroupDetail result) {
             addIncludeBox.setEnabled(true);
@@ -298,7 +298,7 @@
     }
   }
 
-  private class IncludeTable extends FancyFlexTable<AccountGroupInclude> {
+  private class IncludeTable extends FancyFlexTable<AccountGroupIncludeByUuid> {
     private boolean enabled = true;
 
     IncludeTable() {
@@ -314,7 +314,7 @@
     void setEnabled(final boolean enabled) {
       this.enabled = enabled;
       for (int row = 1; row < table.getRowCount(); row++) {
-        final AccountGroupInclude k = getRowItem(row);
+        final AccountGroupIncludeByUuid k = getRowItem(row);
         if (k != null) {
           ((CheckBox) table.getWidget(row, 1)).setEnabled(enabled);
         }
@@ -322,10 +322,10 @@
     }
 
     void deleteChecked() {
-      final HashSet<AccountGroupInclude.Key> keys =
-          new HashSet<AccountGroupInclude.Key>();
+      final HashSet<AccountGroupIncludeByUuid.Key> keys =
+          new HashSet<AccountGroupIncludeByUuid.Key>();
       for (int row = 1; row < table.getRowCount(); row++) {
-        final AccountGroupInclude k = getRowItem(row);
+        final AccountGroupIncludeByUuid k = getRowItem(row);
         if (k != null && ((CheckBox) table.getWidget(row, 1)).getValue()) {
           keys.add(k.getKey());
         }
@@ -335,7 +335,7 @@
             new GerritCallback<VoidResult>() {
               public void onSuccess(final VoidResult result) {
                 for (int row = 1; row < table.getRowCount();) {
-                  final AccountGroupInclude k = getRowItem(row);
+                  final AccountGroupIncludeByUuid k = getRowItem(row);
                   if (k != null && keys.contains(k.getKey())) {
                     table.removeRow(row);
                   } else {
@@ -347,11 +347,11 @@
       }
     }
 
-    void display(final List<AccountGroupInclude> result) {
+    void display(final List<AccountGroupIncludeByUuid> result) {
       while (1 < table.getRowCount())
         table.removeRow(table.getRowCount() - 1);
 
-      for (final AccountGroupInclude k : result) {
+      for (final AccountGroupIncludeByUuid k : result) {
         final int row = table.getRowCount();
         table.insertRow(row);
         applyDataRowStyle(row);
@@ -359,15 +359,18 @@
       }
     }
 
-    void populate(final int row, final AccountGroupInclude k) {
-      AccountGroup.Id id = k.getIncludeId();
-      GroupInfo group = groups.get(id);
+    void populate(final int row, final AccountGroupIncludeByUuid k) {
+      AccountGroup.UUID uuid = k.getIncludeUUID();
       CheckBox checkBox = new CheckBox();
       table.setWidget(row, 1, checkBox);
       checkBox.setEnabled(enabled);
-      table.setWidget(row, 2,
-          new Hyperlink(group.getName(), Dispatcher.toGroup(id)));
-      table.setText(row, 3, groups.get(id).getDescription());
+      if (AccountGroup.isInternalGroup(uuid)) {
+        table.setWidget(row, 2,
+            new Hyperlink(groups.get(uuid).getName(), Dispatcher.toGroup(uuid)));
+        table.setText(row, 3, groups.get(uuid).getDescription());
+      } else {
+        table.setText(row, 2, uuid.get());
+      }
 
       final FlexCellFormatter fmt = table.getFlexCellFormatter();
       fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().iconCell());
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
index aca2e05..67a23f0 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
@@ -27,8 +27,8 @@
 import com.google.gerrit.httpd.rpc.BaseServiceImplementation;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupInclude;
-import com.google.gerrit.reviewdb.client.AccountGroupIncludeAudit;
+import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuid;
+import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuidAudit;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
 import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
 import com.google.gerrit.reviewdb.client.AuthType;
@@ -225,7 +225,8 @@
   }
 
   public void addGroupInclude(final AccountGroup.Id groupId,
-      final String groupName, final AsyncCallback<GroupDetail> callback) {
+      final AccountGroup.UUID incGroupUUID, final String incGroupName,
+      final AsyncCallback<GroupDetail> callback) {
     run(callback, new Action<GroupDetail>() {
       public GroupDetail run(ReviewDb db) throws OrmException, Failure,
           NoSuchGroupException {
@@ -234,21 +235,24 @@
           throw new Failure(new NameAlreadyUsedException());
         }
 
-        final AccountGroup a = findGroup(groupName);
-        if (!control.canAddGroup(a.getId())) {
+        if (incGroupUUID == null) {
+          throw new Failure(new NoSuchGroupException(incGroupName));
+        }
+
+        if (!control.canAddGroup(incGroupUUID)) {
           throw new Failure(new NoSuchEntityException());
         }
 
-        final AccountGroupInclude.Key key =
-            new AccountGroupInclude.Key(groupId, a.getId());
-        AccountGroupInclude m = db.accountGroupIncludes().get(key);
+        final AccountGroupIncludeByUuid.Key key =
+            new AccountGroupIncludeByUuid.Key(groupId, incGroupUUID);
+        AccountGroupIncludeByUuid m = db.accountGroupIncludesByUuid().get(key);
         if (m == null) {
-          m = new AccountGroupInclude(key);
-          db.accountGroupIncludesAudit().insert(
-              Collections.singleton(new AccountGroupIncludeAudit(m,
+          m = new AccountGroupIncludeByUuid(key);
+          db.accountGroupIncludesByUuidAudit().insert(
+              Collections.singleton(new AccountGroupIncludeByUuidAudit(m,
                   getAccountId())));
-          db.accountGroupIncludes().insert(Collections.singleton(m));
-          groupIncludeCache.evictInclude(a.getGroupUUID());
+          db.accountGroupIncludesByUuid().insert(Collections.singleton(m));
+          groupIncludeCache.evictInclude(incGroupUUID);
         }
 
         return groupDetailFactory.create(groupId).call();
@@ -311,7 +315,7 @@
   }
 
   public void deleteGroupIncludes(final AccountGroup.Id groupId,
-      final Set<AccountGroupInclude.Key> keys,
+      final Set<AccountGroupIncludeByUuid.Key> keys,
       final AsyncCallback<VoidResult> callback) {
     run(callback, new Action<VoidResult>() {
       public VoidResult run(final ReviewDb db) throws OrmException,
@@ -321,26 +325,26 @@
           throw new Failure(new NameAlreadyUsedException());
         }
 
-        for (final AccountGroupInclude.Key k : keys) {
+        for (final AccountGroupIncludeByUuid.Key k : keys) {
           if (!groupId.equals(k.getGroupId())) {
             throw new Failure(new NoSuchEntityException());
           }
         }
 
         final Account.Id me = getAccountId();
-        final Set<AccountGroup.Id> groupsToEvict = new HashSet<AccountGroup.Id>();
-        for (final AccountGroupInclude.Key k : keys) {
-          final AccountGroupInclude m =
-              db.accountGroupIncludes().get(k);
+        final Set<AccountGroup.UUID> groupsToEvict = new HashSet<AccountGroup.UUID>();
+        for (final AccountGroupIncludeByUuid.Key k : keys) {
+          final AccountGroupIncludeByUuid m =
+              db.accountGroupIncludesByUuid().get(k);
           if (m != null) {
-            if (!control.canRemoveGroup(m.getIncludeId())) {
+            if (!control.canRemoveGroup(m.getIncludeUUID())) {
               throw new Failure(new NoSuchEntityException());
             }
 
-            AccountGroupIncludeAudit audit = null;
-            for (AccountGroupIncludeAudit a : db
-                .accountGroupIncludesAudit().byGroupInclude(
-                    m.getGroupId(), m.getIncludeId())) {
+            AccountGroupIncludeByUuidAudit audit = null;
+            for (AccountGroupIncludeByUuidAudit a : db
+                .accountGroupIncludesByUuidAudit().byGroupInclude(
+                    m.getGroupId(), m.getIncludeUUID())) {
               if (a.isActive()) {
                 audit = a;
                 break;
@@ -349,15 +353,15 @@
 
             if (audit != null) {
               audit.removed(me);
-              db.accountGroupIncludesAudit().update(
+              db.accountGroupIncludesByUuidAudit().update(
                   Collections.singleton(audit));
             }
-            db.accountGroupIncludes().delete(Collections.singleton(m));
-            groupsToEvict.add(k.getIncludeId());
+            db.accountGroupIncludesByUuid().delete(Collections.singleton(m));
+            groupsToEvict.add(k.getIncludeUUID());
           }
         }
-        for (AccountGroup group : db.accountGroups().get(groupsToEvict)) {
-          groupIncludeCache.evictInclude(group.getGroupUUID());
+        for (AccountGroup.UUID uuid : groupsToEvict) {
+          groupIncludeCache.evictInclude(uuid);
         }
         return VoidResult.INSTANCE;
       }
@@ -408,14 +412,4 @@
       return null;
     }
   }
-
-  private AccountGroup findGroup(final String name) throws OrmException,
-      Failure {
-    final AccountGroup g = groupCache.get(new AccountGroup.NameKey(name));
-    if (g == null) {
-      throw new Failure(new NoSuchGroupException(name));
-    }
-    return g;
-  }
-
 }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupInclude.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupIncludeByUuid.java
similarity index 71%
rename from gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupInclude.java
rename to gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupIncludeByUuid.java
index 4b58689..a5b35ed 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupInclude.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupIncludeByUuid.java
@@ -18,7 +18,7 @@
 import com.google.gwtorm.client.CompoundKey;
 
 /** Membership of an {@link AccountGroup} in an {@link AccountGroup}. */
-public final class AccountGroupInclude {
+public final class AccountGroupIncludeByUuid {
   public static class Key extends CompoundKey<AccountGroup.Id> {
     private static final long serialVersionUID = 1L;
 
@@ -26,16 +26,16 @@
     protected AccountGroup.Id groupId;
 
     @Column(id = 2)
-    protected AccountGroup.Id includeId;
+    protected AccountGroup.UUID includeUUID;
 
     protected Key() {
       groupId = new AccountGroup.Id();
-      includeId = new AccountGroup.Id();
+      includeUUID = new AccountGroup.UUID();
     }
 
-    public Key(final AccountGroup.Id g, final AccountGroup.Id i) {
+    public Key(final AccountGroup.Id g, final AccountGroup.UUID u) {
       groupId = g;
-      includeId = i;
+      includeUUID = u;
     }
 
     @Override
@@ -47,27 +47,27 @@
       return groupId;
     }
 
-    public AccountGroup.Id getIncludeId() {
-      return includeId;
+    public AccountGroup.UUID getIncludeUUID() {
+      return includeUUID;
     }
 
     @Override
     public com.google.gwtorm.client.Key<?>[] members() {
-      return new com.google.gwtorm.client.Key<?>[] {includeId};
+      return new com.google.gwtorm.client.Key<?>[] {includeUUID};
     }
   }
 
   @Column(id = 1, name = Column.NONE)
   protected Key key;
 
-  protected AccountGroupInclude() {
+  protected AccountGroupIncludeByUuid() {
   }
 
-  public AccountGroupInclude(final AccountGroupInclude.Key k) {
+  public AccountGroupIncludeByUuid(final AccountGroupIncludeByUuid.Key k) {
     key = k;
   }
 
-  public AccountGroupInclude.Key getKey() {
+  public AccountGroupIncludeByUuid.Key getKey() {
     return key;
   }
 
@@ -75,7 +75,7 @@
     return key.groupId;
   }
 
-  public AccountGroup.Id getIncludeId() {
-    return key.includeId;
+  public AccountGroup.UUID getIncludeUUID() {
+    return key.includeUUID;
   }
 }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupIncludeAudit.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupIncludeByUuidAudit.java
similarity index 67%
rename from gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupIncludeAudit.java
rename to gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupIncludeByUuidAudit.java
index 275c3c3..6625197 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupIncludeAudit.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupIncludeByUuidAudit.java
@@ -20,7 +20,7 @@
 import java.sql.Timestamp;
 
 /** Inclusion of an {@link AccountGroup} in another {@link AccountGroup}. */
-public final class AccountGroupIncludeAudit {
+public final class AccountGroupIncludeByUuidAudit {
   public static class Key extends CompoundKey<AccountGroup.Id> {
     private static final long serialVersionUID = 1L;
 
@@ -28,19 +28,19 @@
     protected AccountGroup.Id groupId;
 
     @Column(id = 2)
-    protected AccountGroup.Id includeId;
+    protected AccountGroup.UUID includeUUID;
 
     @Column(id = 3)
     protected Timestamp addedOn;
 
     protected Key() {
       groupId = new AccountGroup.Id();
-      includeId = new AccountGroup.Id();
+      includeUUID = new AccountGroup.UUID();
     }
 
-    public Key(final AccountGroup.Id g, final AccountGroup.Id i, final Timestamp t) {
+    public Key(final AccountGroup.Id g, final AccountGroup.UUID u, final Timestamp t) {
       groupId = g;
-      includeId = i;
+      includeUUID = u;
       addedOn = t;
     }
 
@@ -49,8 +49,8 @@
       return groupId;
     }
 
-    public AccountGroup.Id getIncludedId() {
-      return includeId;
+    public AccountGroup.UUID getIncludeUUID() {
+      return includeUUID;
     }
 
     public Timestamp getAddedOn() {
@@ -59,7 +59,7 @@
 
     @Override
     public com.google.gwtorm.client.Key<?>[] members() {
-      return new com.google.gwtorm.client.Key<?>[] {includeId};
+      return new com.google.gwtorm.client.Key<?>[] {includeUUID};
     }
   }
 
@@ -75,18 +75,23 @@
   @Column(id = 4, notNull = false)
   protected Timestamp removedOn;
 
-  protected AccountGroupIncludeAudit() {
+  protected AccountGroupIncludeByUuidAudit() {
   }
 
-  public AccountGroupIncludeAudit(final AccountGroupInclude m,
-      final Account.Id adder) {
+  public AccountGroupIncludeByUuidAudit(final AccountGroupIncludeByUuid m,
+      final Account.Id adder, final Timestamp when) {
     final AccountGroup.Id group = m.getGroupId();
-    final AccountGroup.Id include = m.getIncludeId();
-    key = new AccountGroupIncludeAudit.Key(group, include, now());
+    final AccountGroup.UUID include = m.getIncludeUUID();
+    key = new AccountGroupIncludeByUuidAudit.Key(group, include, when);
     addedBy = adder;
   }
 
-  public AccountGroupIncludeAudit.Key getKey() {
+  public AccountGroupIncludeByUuidAudit(final AccountGroupIncludeByUuid m,
+      final Account.Id adder) {
+    this(m, adder, now());
+  }
+
+  public AccountGroupIncludeByUuidAudit.Key getKey() {
     return key;
   }
 
@@ -99,7 +104,12 @@
     removedOn = now();
   }
 
+  public void removed(final Account.Id deleter, final Timestamp when) {
+    removedBy = deleter;
+    removedOn = when;
+  }
+
   private static Timestamp now() {
     return new Timestamp(System.currentTimeMillis());
   }
-}
\ No newline at end of file
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupIncludeAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupIncludeByUuidAccess.java
similarity index 65%
rename from gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupIncludeAccess.java
rename to gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupIncludeByUuidAccess.java
index 3ee4ba0..78a4128 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupIncludeAccess.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupIncludeByUuidAccess.java
@@ -15,21 +15,21 @@
 package com.google.gerrit.reviewdb.server;
 
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupInclude;
+import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuid;
 import com.google.gwtorm.server.Access;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.PrimaryKey;
 import com.google.gwtorm.server.Query;
 import com.google.gwtorm.server.ResultSet;
 
-public interface AccountGroupIncludeAccess extends
-    Access<AccountGroupInclude, AccountGroupInclude.Key> {
+public interface AccountGroupIncludeByUuidAccess extends
+    Access<AccountGroupIncludeByUuid, AccountGroupIncludeByUuid.Key> {
   @PrimaryKey("key")
-  AccountGroupInclude get(AccountGroupInclude.Key key) throws OrmException;
+  AccountGroupIncludeByUuid get(AccountGroupIncludeByUuid.Key key) throws OrmException;
 
-  @Query("WHERE key.includeId = ?")
-  ResultSet<AccountGroupInclude> byInclude(AccountGroup.Id id) throws OrmException;
+  @Query("WHERE key.includeUUID = ?")
+  ResultSet<AccountGroupIncludeByUuid> byIncludeUUID(AccountGroup.UUID uuid) throws OrmException;
 
   @Query("WHERE key.groupId = ?")
-  ResultSet<AccountGroupInclude> byGroup(AccountGroup.Id id) throws OrmException;
+  ResultSet<AccountGroupIncludeByUuid> byGroup(AccountGroup.Id id) throws OrmException;
 }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupIncludeAuditAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupIncludeByUuidAuditAccess.java
similarity index 66%
rename from gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupIncludeAuditAccess.java
rename to gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupIncludeByUuidAuditAccess.java
index b3f4f88..1c95f75 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupIncludeAuditAccess.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupIncludeByUuidAuditAccess.java
@@ -15,20 +15,20 @@
 package com.google.gerrit.reviewdb.server;
 
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupIncludeAudit;
+import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuidAudit;
 import com.google.gwtorm.server.Access;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.PrimaryKey;
 import com.google.gwtorm.server.Query;
 import com.google.gwtorm.server.ResultSet;
 
-public interface AccountGroupIncludeAuditAccess extends
-    Access<AccountGroupIncludeAudit, AccountGroupIncludeAudit.Key> {
+public interface AccountGroupIncludeByUuidAuditAccess extends
+    Access<AccountGroupIncludeByUuidAudit, AccountGroupIncludeByUuidAudit.Key> {
   @PrimaryKey("key")
-  AccountGroupIncludeAudit get(AccountGroupIncludeAudit.Key key)
+  AccountGroupIncludeByUuidAudit get(AccountGroupIncludeByUuidAudit.Key key)
       throws OrmException;
 
-  @Query("WHERE key.groupId = ? AND key.includeId = ?")
-  ResultSet<AccountGroupIncludeAudit> byGroupInclude(AccountGroup.Id groupId,
-      AccountGroup.Id incGroupId) throws OrmException;
+  @Query("WHERE key.groupId = ? AND key.includeUUID = ?")
+  ResultSet<AccountGroupIncludeByUuidAudit> byGroupInclude(AccountGroup.Id groupId,
+      AccountGroup.UUID incGroupUUID) throws OrmException;
 }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDb.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDb.java
index 149626b..87bbd5b 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDb.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDb.java
@@ -70,12 +70,6 @@
   @Relation(id = 13)
   AccountGroupMemberAuditAccess accountGroupMembersAudit();
 
-  @Relation(id = 14)
-  AccountGroupIncludeAccess accountGroupIncludes();
-
-  @Relation(id = 15)
-  AccountGroupIncludeAuditAccess accountGroupIncludesAudit();
-
   @Relation(id = 17)
   AccountDiffPreferenceAccess accountDiffPreferences();
 
@@ -112,6 +106,12 @@
   @Relation(id = 28)
   SubmoduleSubscriptionAccess submoduleSubscriptions();
 
+  @Relation(id = 29)
+  AccountGroupIncludeByUuidAccess accountGroupIncludesByUuid();
+
+  @Relation(id = 30)
+  AccountGroupIncludeByUuidAuditAccess accountGroupIncludesByUuidAudit();
+
   /** Create the next unique id for an {@link Account}. */
   @Sequence(startWith = 1000000)
   int nextAccountId() throws OrmException;
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
index 9d453fc..d6609e7 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
@@ -34,10 +34,10 @@
 
 
 -- *********************************************************************
--- AccountGroupIncludeAccess
+-- AccountGroupIncludeByUuidAccess
 --    @PrimaryKey covers: byGroup
-CREATE INDEX account_group_includes_byInclude
-ON account_group_includes (include_id);
+CREATE INDEX account_group_includes_by_uuid_byInclude
+ON account_group_includes_by_uuid (include_uuid);
 
 
 -- *********************************************************************
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
index 675c5bc..3b62e84 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
@@ -82,10 +82,10 @@
 
 
 -- *********************************************************************
--- AccountGroupIncludeAccess
+-- AccountGroupIncludeByUuidAccess
 --    @PrimaryKey covers: byGroup
-CREATE INDEX account_group_includes_byInclude
-ON account_group_includes (include_id);
+CREATE INDEX account_group_includes_by_uuid_byInclude
+ON account_group_includes_by_uuid (include_uuid);
 
 
 -- *********************************************************************
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
index d9b12ac..9141566 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
@@ -123,15 +123,15 @@
     return canSeeMembers();
   }
 
-  public boolean canAddGroup(AccountGroup.Id id) {
+  public boolean canAddGroup(AccountGroup.UUID uuid) {
     return isOwner();
   }
 
-  public boolean canRemoveGroup(AccountGroup.Id id) {
+  public boolean canRemoveGroup(AccountGroup.UUID uuid) {
     return isOwner();
   }
 
-  public boolean canSeeGroup(AccountGroup.Id id) {
+  public boolean canSeeGroup(AccountGroup.UUID uuid) {
     return canSeeMembers();
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupDetailFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupDetailFactory.java
index 0aef40c..4d2cc1a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupDetailFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupDetailFactory.java
@@ -20,7 +20,7 @@
 import com.google.gerrit.common.errors.NoSuchGroupException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupInclude;
+import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuid;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gwtorm.server.OrmException;
@@ -123,21 +123,21 @@
     return members;
   }
 
-  private List<AccountGroupInclude> loadIncludes() throws OrmException {
-    List<AccountGroupInclude> groups = new ArrayList<AccountGroupInclude>();
+  private List<AccountGroupIncludeByUuid> loadIncludes() throws OrmException {
+    List<AccountGroupIncludeByUuid> groups = new ArrayList<AccountGroupIncludeByUuid>();
 
-    for (final AccountGroupInclude m : db.accountGroupIncludes().byGroup(groupId)) {
-      if (control.canSeeGroup(m.getIncludeId())) {
-        gic.want(m.getIncludeId());
+    for (final AccountGroupIncludeByUuid m : db.accountGroupIncludesByUuid().byGroup(groupId)) {
+      if (control.canSeeGroup(m.getIncludeUUID())) {
+        gic.want(m.getIncludeUUID());
         groups.add(m);
       }
     }
 
-    Collections.sort(groups, new Comparator<AccountGroupInclude>() {
-      public int compare(final AccountGroupInclude o1,
-          final AccountGroupInclude o2) {
-        final AccountGroup a = gic.get(o1.getIncludeId());
-        final AccountGroup b = gic.get(o2.getIncludeId());
+    Collections.sort(groups, new Comparator<AccountGroupIncludeByUuid>() {
+      public int compare(final AccountGroupIncludeByUuid o1,
+          final AccountGroupIncludeByUuid o2) {
+        final AccountGroup a = gic.get(o1.getIncludeUUID());
+        final AccountGroup b = gic.get(o2.getIncludeUUID());
         return n(a).compareTo(n(b));
       }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
index 7fbba45..ba78210 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
@@ -19,7 +19,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupInclude;
+import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuid;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.cache.CacheModule;
 import com.google.gwtorm.server.SchemaFactory;
@@ -102,8 +102,8 @@
         }
 
         Set<AccountGroup.Id> ids = Sets.newHashSet();
-        for (AccountGroupInclude agi : db.accountGroupIncludes()
-            .byInclude(group.get(0).getId())) {
+        for (AccountGroupIncludeByUuid agi : db.accountGroupIncludesByUuid()
+            .byIncludeUUID(group.get(0).getGroupUUID())) {
           ids.add(agi.getGroupId());
         }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupInfoCacheFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupInfoCacheFactory.java
index 19d953d..4c399c4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupInfoCacheFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupInfoCacheFactory.java
@@ -31,35 +31,35 @@
   }
 
   private final GroupCache groupCache;
-  private final Map<AccountGroup.Id, AccountGroup> out;
+  private final Map<AccountGroup.UUID, AccountGroup> out;
 
   @Inject
   GroupInfoCacheFactory(final GroupCache groupCache) {
     this.groupCache = groupCache;
-    this.out = new HashMap<AccountGroup.Id, AccountGroup>();
+    this.out = new HashMap<AccountGroup.UUID, AccountGroup>();
   }
 
   /**
    * Indicate a group will be needed later on.
    *
-   * @param id identity that will be needed in the future; may be null.
+   * @param uuid identity that will be needed in the future; may be null.
    */
-  public void want(final AccountGroup.Id id) {
-    if (id != null && !out.containsKey(id)) {
-      out.put(id, groupCache.get(id));
+  public void want(final AccountGroup.UUID uuid) {
+    if (uuid != null && !out.containsKey(uuid)) {
+      out.put(uuid, groupCache.get(uuid));
     }
   }
 
   /** Indicate one or more groups will be needed later on. */
-  public void want(final Iterable<AccountGroup.Id> ids) {
-    for (final AccountGroup.Id id : ids) {
-      want(id);
+  public void want(final Iterable<AccountGroup.UUID> uuids) {
+    for (final AccountGroup.UUID uuid : uuids) {
+      want(uuid);
     }
   }
 
-  public AccountGroup get(final AccountGroup.Id id) {
-    want(id);
-    return out.get(id);
+  public AccountGroup get(final AccountGroup.UUID uuid) {
+    want(uuid);
+    return out.get(uuid);
   }
 
   /**
@@ -68,6 +68,7 @@
   public GroupInfoCache create() {
     final List<GroupInfo> r = new ArrayList<GroupInfo>(out.size());
     for (final AccountGroup a : out.values()) {
+      if (a == null) continue;
       r.add(new GroupInfo(a));
     }
     return new GroupInfoCache(r);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembers.java
index 84282b5..16fcc0b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembers.java
@@ -18,7 +18,7 @@
 import com.google.gerrit.common.errors.NoSuchGroupException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupInclude;
+import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuid;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.IdentifiedUser;
@@ -111,9 +111,9 @@
       }
     }
     if (groupDetail.includes != null) {
-      for (final AccountGroupInclude groupInclude : groupDetail.includes) {
+      for (final AccountGroupIncludeByUuid groupInclude : groupDetail.includes) {
         final AccountGroup includedGroup =
-            groupCache.get(groupInclude.getIncludeId());
+            groupCache.get(groupInclude.getIncludeUUID());
         if (!seen.contains(includedGroup.getGroupUUID())) {
           members.addAll(listAccounts(includedGroup.getGroupUUID(), project, seen));
         }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java
index 1ff1a3e..0de2b044 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java
@@ -18,8 +18,8 @@
 import com.google.gerrit.common.errors.PermissionDeniedException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupInclude;
-import com.google.gerrit.reviewdb.client.AccountGroupIncludeAudit;
+import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuid;
+import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuidAudit;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
 import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
 import com.google.gerrit.reviewdb.client.AccountGroupName;
@@ -35,7 +35,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 
 public class PerformCreateGroup {
@@ -89,7 +88,7 @@
       final String groupDescription, final boolean visibleToAll,
       final AccountGroup.Id ownerGroupId,
       final Collection<? extends Account.Id> initialMembers,
-      final Collection<? extends AccountGroup.Id> initialGroups)
+      final Collection<? extends AccountGroup.UUID> initialGroups)
       throws OrmException, NameAlreadyUsedException, PermissionDeniedException {
     if (!currentUser.getCapabilities().canCreateGroup()) {
       throw new PermissionDeniedException(String.format(
@@ -160,26 +159,25 @@
   }
 
   private void addGroups(final AccountGroup.Id groupId,
-      final Collection<? extends AccountGroup.Id> groups) throws OrmException {
-    final List<AccountGroupInclude> includeList =
-      new ArrayList<AccountGroupInclude>();
-    final List<AccountGroupIncludeAudit> includesAudit =
-      new ArrayList<AccountGroupIncludeAudit>();
-    for (AccountGroup.Id includeId : groups) {
-      final AccountGroupInclude groupInclude =
-        new AccountGroupInclude(new AccountGroupInclude.Key(groupId, includeId));
+      final Collection<? extends AccountGroup.UUID> groups) throws OrmException {
+    final List<AccountGroupIncludeByUuid> includeList =
+      new ArrayList<AccountGroupIncludeByUuid>();
+    final List<AccountGroupIncludeByUuidAudit> includesAudit =
+      new ArrayList<AccountGroupIncludeByUuidAudit>();
+    for (AccountGroup.UUID includeUUID : groups) {
+      final AccountGroupIncludeByUuid groupInclude =
+        new AccountGroupIncludeByUuid(new AccountGroupIncludeByUuid.Key(groupId, includeUUID));
       includeList.add(groupInclude);
 
-      final AccountGroupIncludeAudit audit =
-        new AccountGroupIncludeAudit(groupInclude, currentUser.getAccountId());
+      final AccountGroupIncludeByUuidAudit audit =
+        new AccountGroupIncludeByUuidAudit(groupInclude, currentUser.getAccountId());
       includesAudit.add(audit);
     }
-    db.accountGroupIncludes().insert(includeList);
-    db.accountGroupIncludesAudit().insert(includesAudit);
+    db.accountGroupIncludesByUuid().insert(includeList);
+    db.accountGroupIncludesByUuidAudit().insert(includesAudit);
 
-    for (AccountGroup group : db.accountGroups().get(
-        new HashSet<AccountGroup.Id>(groups))) {
-      groupIncludeCache.evictInclude(group.getGroupUUID());
+    for (AccountGroup.UUID uuid : groups) {
+      groupIncludeCache.evictInclude(uuid);
     }
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
index 3561c0c..1274016 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
@@ -20,7 +20,7 @@
 import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupInclude;
+import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuid;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
 import com.google.gerrit.reviewdb.client.AccountProjectWatch;
 import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
@@ -59,6 +59,7 @@
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Queue;
 import java.util.Set;
 import java.util.TreeSet;
@@ -476,10 +477,15 @@
           .byGroup(next)) {
         matching.accounts.add(m.getAccountId());
       }
-      for (AccountGroupInclude m : args.db.get().accountGroupIncludes()
+      for (AccountGroupIncludeByUuid m : args.db.get().accountGroupIncludesByUuid()
           .byGroup(next)) {
-        if (seen.add(m.getIncludeId())) {
-          scan.add(m.getIncludeId());
+        List<AccountGroup> incGroup = args.db.get().accountGroups().
+            byUUID(m.getIncludeUUID()).toList();
+        if (incGroup.size() == 1) {
+          AccountGroup.Id includeId = incGroup.get(0).getId();
+          if (seen.add(includeId)) {
+            scan.add(includeId);
+          }
         }
       }
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
index 89e6ee6..1019d33 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -32,7 +32,7 @@
 /** A version of the database schema. */
 public abstract class SchemaVersion {
   /** The current schema version. */
-  public static final Class<Schema_73> C = Schema_73.class;
+  public static final Class<Schema_74> C = Schema_74.class;
 
   public static class Module extends AbstractModule {
     @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_57.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_57.java
index 3a288e2..2ab83c8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_57.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_57.java
@@ -30,6 +30,7 @@
 import com.google.gerrit.server.git.LocalDiskRepositoryManager;
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gwtorm.jdbc.JdbcSchema;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -41,6 +42,8 @@
 import org.eclipse.jgit.util.FS;
 
 import java.io.IOException;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
 import java.util.Collections;
 
 public class Schema_57 extends SchemaVersion {
@@ -95,6 +98,11 @@
 
         // Move the repository.*.createGroup to Create Project.
         String[] createGroupList = cfg.getStringList("repository", "*", "createGroup");
+
+        // Prepare the account_group_includes query
+        PreparedStatement stmt = ((JdbcSchema) db).getConnection().
+            prepareStatement("SELECT * FROM account_group_includes WHERE group_id = ?");
+
         for (String name : createGroupList) {
           AccountGroup.NameKey key = new AccountGroup.NameKey(name);
           AccountGroupName groupName = db.accountGroupNames().get(key);
@@ -117,9 +125,10 @@
         }
 
         AccountGroup batch = db.accountGroups().get(sc.batchUsersGroupId);
+        stmt.setInt(0, sc.batchUsersGroupId.get());
         if (batch != null
             && db.accountGroupMembers().byGroup(sc.batchUsersGroupId).toList().isEmpty()
-            &&  db.accountGroupIncludes().byGroup(sc.batchUsersGroupId).toList().isEmpty()) {
+            &&  stmt.executeQuery().first() != false) {
           // If the batch user group is not used, delete it.
           //
           db.accountGroups().delete(Collections.singleton(batch));
@@ -136,6 +145,8 @@
 
         md.setMessage("Upgrade to Gerrit Code Review schema 57\n");
         config.commit(md);
+      } catch (SQLException err) {
+        throw new OrmException( "Cannot read account_group_includes", err);
       } finally {
         git.close();
       }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_74.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_74.java
new file mode 100644
index 0000000..ca012d1
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_74.java
@@ -0,0 +1,114 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.schema;
+
+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.AccountGroupIncludeByUuidAudit;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gwtorm.jdbc.JdbcSchema;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/* Handles copying all entries from AccountGroupIncludes(Audit) to the new tables */
+public class Schema_74 extends SchemaVersion {
+  @Inject
+  Schema_74(Provider<Schema_73> prior) {
+    super(prior);
+  }
+
+  @Override
+  protected void migrateData(final ReviewDb db, final UpdateUI ui)
+      throws SQLException, OrmException {
+    // Grab all the groups since we don't have the cache available
+    HashMap<AccountGroup.Id, AccountGroup.UUID> allGroups =
+        new HashMap<AccountGroup.Id, AccountGroup.UUID>();
+    for( AccountGroup ag : db.accountGroups().all() ) {
+      allGroups.put(ag.getId(), ag.getGroupUUID());
+    }
+
+    // Initialize some variables
+    Connection conn = ((JdbcSchema) db).getConnection();
+    ArrayList<AccountGroupIncludeByUuid> newIncludes =
+        new ArrayList<AccountGroupIncludeByUuid>();
+    ArrayList<AccountGroupIncludeByUuidAudit> newIncludeAudits =
+        new ArrayList<AccountGroupIncludeByUuidAudit>();
+
+    // Iterate over all entries in account_group_includes
+    Statement oldGroupIncludesStmt = conn.createStatement();
+    ResultSet oldGroupIncludes = oldGroupIncludesStmt.
+        executeQuery("SELECT * FROM account_group_includes");
+    while (oldGroupIncludes.next()) {
+      AccountGroup.Id oldGroupId =
+          new AccountGroup.Id(oldGroupIncludes.getInt("group_id"));
+      AccountGroup.Id oldIncludeId =
+          new AccountGroup.Id(oldGroupIncludes.getInt("include_id"));
+      AccountGroup.UUID uuidFromIncludeId = allGroups.get(oldIncludeId);
+
+      // If we've got an include, but the group no longer exists, don't bother converting
+      if (uuidFromIncludeId == null) {
+        ui.message("Skipping group_id = \"" + oldIncludeId.get() +
+            "\", not a current group");
+        continue;
+      }
+
+      // Create the new include entry
+      AccountGroupIncludeByUuid destIncludeEntry = new AccountGroupIncludeByUuid(
+          new AccountGroupIncludeByUuid.Key(oldGroupId, uuidFromIncludeId));
+
+      // Iterate over all the audits (for this group)
+      PreparedStatement oldAuditsQuery = conn.prepareStatement(
+          "SELECT * FROM account_group_includes_audit WHERE group_id=? AND include_id=?");
+      oldAuditsQuery.setInt(1, oldGroupId.get());
+      oldAuditsQuery.setInt(2, oldIncludeId.get());
+      ResultSet oldGroupIncludeAudits = oldAuditsQuery.executeQuery();
+      while (oldGroupIncludeAudits.next()) {
+        Account.Id addedBy = new Account.Id(oldGroupIncludeAudits.getInt("added_by"));
+        int removedBy = oldGroupIncludeAudits.getInt("removed_by");
+
+        // Create the new audit entry
+        AccountGroupIncludeByUuidAudit destAuditEntry =
+            new AccountGroupIncludeByUuidAudit(destIncludeEntry, addedBy,
+                oldGroupIncludeAudits.getTimestamp("added_on"));
+
+        // If this was a "removed on" entry, note that
+        if (removedBy > 0) {
+          destAuditEntry.removed(new Account.Id(removedBy),
+              oldGroupIncludeAudits.getTimestamp("removed_on"));
+        }
+        newIncludeAudits.add(destAuditEntry);
+      }
+      newIncludes.add(destIncludeEntry);
+      oldAuditsQuery.close();
+      oldGroupIncludeAudits.close();
+    }
+    oldGroupIncludes.close();
+    oldGroupIncludesStmt.close();
+
+    // Now insert all of the new entries to the database
+    db.accountGroupIncludesByUuid().insert(newIncludes);
+    db.accountGroupIncludesByUuidAudit().insert(newIncludeAudits);
+  }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
index 728c20c..da08395 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
@@ -57,10 +57,10 @@
   @Option(name = "--visible-to-all", usage = "to make the group visible to all registered users")
   private boolean visibleToAll;
 
-  private final Set<AccountGroup.Id> initialGroups = new HashSet<AccountGroup.Id>();
+  private final Set<AccountGroup.UUID> initialGroups = new HashSet<AccountGroup.UUID>();
 
   @Option(name = "--group", aliases = "-g", metaVar = "GROUP", usage = "initial set of groups to be included in the group")
-  void addGroup(final AccountGroup.Id id) {
+  void addGroup(final AccountGroup.UUID id) {
     initialGroups.add(id);
   }