Add manifest groups

Allows specifying a list of groups with a -g argument to repo init.
The groups act on a group= attribute specified on projects in the
manifest.
All projects are implicitly labelled with "default" unless they are
explicitly labelled "-default".
Prefixing a group with "-" removes matching projects from the list
of projects to sync.
If any non-inverted manifest groups are specified, the default label
is ignored.

Change-Id: I3a0dd7a93a8a1756205de1d03eee8c00906af0e5
Reviewed-on: https://gerrit-review.googlesource.com/34570
Reviewed-by: Shawn Pearce <sop@google.com>
Tested-by: Shawn Pearce <sop@google.com>
diff --git a/manifest_xml.py b/manifest_xml.py
index 4453869..a250382 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -119,6 +119,12 @@
   def Save(self, fd, peg_rev=False):
     """Write the current manifest out to the given file descriptor.
     """
+    mp = self.manifestProject
+
+    groups = mp.config.GetString('manifest.groups')
+    if groups:
+      groups = re.split('[,\s]+', groups)
+
     doc = xml.dom.minidom.Document()
     root = doc.createElement('manifest')
     doc.appendChild(root)
@@ -167,6 +173,10 @@
 
     for p in sort_projects:
       p = self.projects[p]
+
+      if not p.MatchesGroups(groups):
+        continue
+
       e = doc.createElement('project')
       root.appendChild(e)
       e.setAttribute('name', p.name)
@@ -190,6 +200,9 @@
         ce.setAttribute('dest', c.dest)
         e.appendChild(ce)
 
+      if p.groups:
+        e.setAttribute('groups', ','.join(p.groups))
+
     if self._repo_hooks_project:
       root.appendChild(doc.createTextNode(''))
       e = doc.createElement('repo-hooks')
@@ -504,6 +517,12 @@
     else:
       rebase = rebase.lower() in ("yes", "true", "1")
 
+    groups = node.getAttribute('groups')
+    if groups:
+      groups = re.split('[,\s]+', groups)
+    else:
+      groups = None
+
     if self.IsMirror:
       relpath = None
       worktree = None
@@ -520,7 +539,8 @@
                       relpath = path,
                       revisionExpr = revisionExpr,
                       revisionId = None,
-                      rebase = rebase)
+                      rebase = rebase,
+                      groups = groups)
 
     for n in node.childNodes:
       if n.nodeName == 'copyfile':