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>
This commit is contained in:
Colin Cross 2012-03-28 20:15:45 -07:00 committed by Shawn Pearce
parent d67872d2f4
commit 5acde75e5d
8 changed files with 108 additions and 7 deletions

View File

@ -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)

View File

@ -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
---------------------- ----------------------

View File

@ -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

View File

@ -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':

View File

@ -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:

6
repo
View File

@ -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

View File

@ -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)

View File

@ -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": \