mirror of
https://gerrit.googlesource.com/git-repo
synced 2024-12-21 07:16:21 +00:00
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:
parent
d67872d2f4
commit
5acde75e5d
13
command.py
13
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)
|
||||||
|
|
||||||
|
@ -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
error.py
12
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
|
||||||
|
@ -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':
|
||||||
|
46
project.py
46
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:
|
||||||
|
6
repo
6
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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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": \
|
||||||
|
Loading…
Reference in New Issue
Block a user