Просмотр исходного кода

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>
Colin Cross 14 лет назад
Родитель
Сommit
5acde75e5d
8 измененных файлов с 108 добавлено и 7 удалено
  1. 12 1
      command.py
  2. 5 0
      docs/manifest-format.txt
  3. 12 0
      error.py
  4. 21 1
      manifest_xml.py
  5. 44 2
      project.py
  6. 5 1
      repo
  7. 6 0
      subcmds/init.py
  8. 3 2
      subcmds/sync.py

+ 12 - 1
command.py

@@ -15,9 +15,11 @@
 
 
 import os
 import os
 import optparse
 import optparse
+import re
 import sys
 import sys
 
 
 from error import NoSuchProjectError
 from error import NoSuchProjectError
+from error import InvalidProjectGroupsError
 
 
 class Command(object):
 class Command(object):
   """Base class for any command line action in repo.
   """Base class for any command line action in repo.
@@ -63,9 +65,16 @@ class Command(object):
     all = self.manifest.projects
     all = self.manifest.projects
     result = []
     result = []
 
 
+    mp = self.manifest.manifestProject
+
+    groups = mp.config.GetString('manifest.groups')
+    if groups:
+      groups = re.split('[,\s]+', groups)
+
     if not args:
     if not args:
       for project in all.values():
       for project in all.values():
-        if missing_ok or project.Exists:
+        if ((missing_ok or project.Exists) and
+            project.MatchesGroups(groups)):
           result.append(project)
           result.append(project)
     else:
     else:
       by_path = None
       by_path = None
@@ -102,6 +111,8 @@ class Command(object):
           raise NoSuchProjectError(arg)
           raise NoSuchProjectError(arg)
         if not missing_ok and not project.Exists:
         if not missing_ok and not project.Exists:
           raise NoSuchProjectError(arg)
           raise NoSuchProjectError(arg)
+        if not project.MatchesGroups(groups):
+          raise InvalidProjectGroupsError(arg)
 
 
         result.append(project)
         result.append(project)
 
 

+ 5 - 0
docs/manifest-format.txt

@@ -48,6 +48,7 @@ following DTD:
     <!ATTLIST project path     CDATA #IMPLIED>
     <!ATTLIST project path     CDATA #IMPLIED>
     <!ATTLIST project remote   IDREF #IMPLIED>
     <!ATTLIST project remote   IDREF #IMPLIED>
     <!ATTLIST project revision CDATA #IMPLIED>
     <!ATTLIST project revision CDATA #IMPLIED>
+    <!ATTLIST project groups   CDATA #IMPLIED>
   
   
     <!ELEMENT remove-project (EMPTY)>
     <!ELEMENT remove-project (EMPTY)>
     <!ATTLIST remove-project name  CDATA #REQUIRED>
     <!ATTLIST remove-project name  CDATA #REQUIRED>
@@ -158,6 +159,10 @@ Tags and/or explicit SHA-1s should work in theory, but have not
 been extensively tested.  If not supplied the revision given by
 been extensively tested.  If not supplied the revision given by
 the default element is used.
 the default element is used.
 
 
+Attribute `groups`: List of groups to which this project belongs,
+whitespace or comma separated.  All projects are part of the group
+"default" unless "-default" is specified in the list of groups.
+
 Element remove-project
 Element remove-project
 ----------------------
 ----------------------
 
 

+ 12 - 0
error.py

@@ -77,6 +77,18 @@ class NoSuchProjectError(Exception):
       return 'in current directory'
       return 'in current directory'
     return self.name
     return self.name
 
 
+
+class InvalidProjectGroupsError(Exception):
+  """A specified project is not suitable for the specified groups
+  """
+  def __init__(self, name=None):
+    self.name = name
+
+  def __str__(self):
+    if self.Name is None:
+      return 'in current directory'
+    return self.name
+
 class RepoChangedException(Exception):
 class RepoChangedException(Exception):
   """Thrown if 'repo sync' results in repo updating its internal
   """Thrown if 'repo sync' results in repo updating its internal
      repo or manifest repositories.  In this special case we must
      repo or manifest repositories.  In this special case we must

+ 21 - 1
manifest_xml.py

@@ -119,6 +119,12 @@ class XmlManifest(object):
   def Save(self, fd, peg_rev=False):
   def Save(self, fd, peg_rev=False):
     """Write the current manifest out to the given file descriptor.
     """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()
     doc = xml.dom.minidom.Document()
     root = doc.createElement('manifest')
     root = doc.createElement('manifest')
     doc.appendChild(root)
     doc.appendChild(root)
@@ -167,6 +173,10 @@ class XmlManifest(object):
 
 
     for p in sort_projects:
     for p in sort_projects:
       p = self.projects[p]
       p = self.projects[p]
+
+      if not p.MatchesGroups(groups):
+        continue
+
       e = doc.createElement('project')
       e = doc.createElement('project')
       root.appendChild(e)
       root.appendChild(e)
       e.setAttribute('name', p.name)
       e.setAttribute('name', p.name)
@@ -190,6 +200,9 @@ class XmlManifest(object):
         ce.setAttribute('dest', c.dest)
         ce.setAttribute('dest', c.dest)
         e.appendChild(ce)
         e.appendChild(ce)
 
 
+      if p.groups:
+        e.setAttribute('groups', ','.join(p.groups))
+
     if self._repo_hooks_project:
     if self._repo_hooks_project:
       root.appendChild(doc.createTextNode(''))
       root.appendChild(doc.createTextNode(''))
       e = doc.createElement('repo-hooks')
       e = doc.createElement('repo-hooks')
@@ -504,6 +517,12 @@ class XmlManifest(object):
     else:
     else:
       rebase = rebase.lower() in ("yes", "true", "1")
       rebase = rebase.lower() in ("yes", "true", "1")
 
 
+    groups = node.getAttribute('groups')
+    if groups:
+      groups = re.split('[,\s]+', groups)
+    else:
+      groups = None
+
     if self.IsMirror:
     if self.IsMirror:
       relpath = None
       relpath = None
       worktree = None
       worktree = None
@@ -520,7 +539,8 @@ class XmlManifest(object):
                       relpath = path,
                       relpath = path,
                       revisionExpr = revisionExpr,
                       revisionExpr = revisionExpr,
                       revisionId = None,
                       revisionId = None,
-                      rebase = rebase)
+                      rebase = rebase,
+                      groups = groups)
 
 
     for n in node.childNodes:
     for n in node.childNodes:
       if n.nodeName == 'copyfile':
       if n.nodeName == 'copyfile':

+ 44 - 2
project.py

@@ -504,7 +504,8 @@ class Project(object):
                relpath,
                relpath,
                revisionExpr,
                revisionExpr,
                revisionId,
                revisionId,
-               rebase = True):
+               rebase = True,
+               groups = None):
     self.manifest = manifest
     self.manifest = manifest
     self.name = name
     self.name = name
     self.remote = remote
     self.remote = remote
@@ -524,6 +525,7 @@ class Project(object):
       self.revisionId = revisionId
       self.revisionId = revisionId
 
 
     self.rebase = rebase
     self.rebase = rebase
+    self.groups = groups
 
 
     self.snapshots = {}
     self.snapshots = {}
     self.copyfiles = []
     self.copyfiles = []
@@ -645,6 +647,45 @@ class Project(object):
 
 
     return heads
     return heads
 
 
+  def MatchesGroups(self, manifest_groups):
+    """Returns true if the manifest groups specified at init should cause
+       this project to be synced.
+       Prefixing a manifest group with "-" inverts the meaning of a group.
+       All projects are implicitly labelled with "default" unless they are
+       explicitly labelled "-default".
+       If any non-inverted manifest groups are specified, the default label
+       is ignored.
+       Specifying only inverted groups implies "default".
+    """
+    project_groups = self.groups
+    if not manifest_groups:
+      return not project_groups or not "-default" in project_groups
+
+    if not project_groups:
+      project_groups = ["default"]
+    elif not ("default" in project_groups or "-default" in project_groups):
+      project_groups.append("default")
+
+    plus_groups = [x for x in manifest_groups if not x.startswith("-")]
+    minus_groups = [x[1:] for x in manifest_groups if x.startswith("-")]
+
+    if not plus_groups:
+      plus_groups.append("default")
+
+    for group in minus_groups:
+      if group in project_groups:
+        # project was excluded by -group
+        return False
+
+    for group in plus_groups:
+      if group in project_groups:
+        # project was included by group
+        return True
+
+    # groups were specified that did not include this project
+    if plus_groups:
+      return False
+    return True
 
 
 ## Status Display ##
 ## Status Display ##
 
 
@@ -2091,7 +2132,8 @@ class MetaProject(Project):
                      remote = RemoteSpec('origin'),
                      remote = RemoteSpec('origin'),
                      relpath = '.repo/%s' % name,
                      relpath = '.repo/%s' % name,
                      revisionExpr = 'refs/heads/master',
                      revisionExpr = 'refs/heads/master',
-                     revisionId = None)
+                     revisionId = None,
+                     groups = None)
 
 
   def PreSync(self):
   def PreSync(self):
     if self.Exists:
     if self.Exists:

+ 5 - 1
repo

@@ -28,7 +28,7 @@ if __name__ == '__main__':
 del magic
 del magic
 
 
 # increment this whenever we make important changes to this script
 # increment this whenever we make important changes to this script
-VERSION = (1, 14)
+VERSION = (1, 15)
 
 
 # increment this if the MAINTAINER_KEYS block is modified
 # increment this if the MAINTAINER_KEYS block is modified
 KEYRING_VERSION = (1,0)
 KEYRING_VERSION = (1,0)
@@ -125,6 +125,10 @@ group.add_option('--reference',
 group.add_option('--depth', type='int', default=None,
 group.add_option('--depth', type='int', default=None,
                  dest='depth',
                  dest='depth',
                  help='create a shallow clone with given depth; see git clone')
                  help='create a shallow clone with given depth; see git clone')
+group.add_option('-g', '--groups',
+                 dest='groups', default="",
+                 help='restrict manifest projects to ones with a specified group',
+                 metavar='GROUP')
 
 
 
 
 # Tool
 # Tool

+ 6 - 0
subcmds/init.py

@@ -86,6 +86,10 @@ to update the working directory files.
     g.add_option('--depth', type='int', default=None,
     g.add_option('--depth', type='int', default=None,
                  dest='depth',
                  dest='depth',
                  help='create a shallow clone with given depth; see git clone')
                  help='create a shallow clone with given depth; see git clone')
+    g.add_option('-g', '--groups',
+                 dest='groups', default="",
+                 help='restrict manifest projects to ones with a specified group',
+                 metavar='GROUP')
 
 
     # Tool
     # Tool
     g = p.add_option_group('repo Version options')
     g = p.add_option_group('repo Version options')
@@ -135,6 +139,8 @@ to update the working directory files.
       r.ResetFetch()
       r.ResetFetch()
       r.Save()
       r.Save()
 
 
+    m.config.SetString('manifest.groups', opt.groups)
+
     if opt.reference:
     if opt.reference:
       m.config.SetString('repo.reference', opt.reference)
       m.config.SetString('repo.reference', opt.reference)
 
 

+ 3 - 2
subcmds/sync.py

@@ -277,7 +277,7 @@ later is required to fix a server side protocol bug.
 
 
   def UpdateProjectList(self):
   def UpdateProjectList(self):
     new_project_paths = []
     new_project_paths = []
-    for project in self.manifest.projects.values():
+    for project in self.GetProjects(None, missing_ok=True):
       if project.relpath:
       if project.relpath:
         new_project_paths.append(project.relpath)
         new_project_paths.append(project.relpath)
     file_name = 'project.list'
     file_name = 'project.list'
@@ -306,7 +306,8 @@ later is required to fix a server side protocol bug.
                              worktree = os.path.join(self.manifest.topdir, path),
                              worktree = os.path.join(self.manifest.topdir, path),
                              relpath = path,
                              relpath = path,
                              revisionExpr = 'HEAD',
                              revisionExpr = 'HEAD',
-                             revisionId = None)
+                             revisionId = None,
+                             groups = None)
 
 
               if project.IsDirty():
               if project.IsDirty():
                 print >>sys.stderr, 'error: Cannot remove project "%s": \
                 print >>sys.stderr, 'error: Cannot remove project "%s": \