diff options
-rw-r--r-- | command.py | 13 | ||||
-rw-r--r-- | docs/manifest-format.txt | 5 | ||||
-rw-r--r-- | error.py | 12 | ||||
-rw-r--r-- | manifest_xml.py | 22 | ||||
-rw-r--r-- | project.py | 46 | ||||
-rwxr-xr-x | repo | 6 | ||||
-rw-r--r-- | subcmds/init.py | 6 | ||||
-rw-r--r-- | subcmds/sync.py | 5 |
8 files changed, 108 insertions, 7 deletions
@@ -15,9 +15,11 @@ | |||
15 | 15 | ||
16 | import os | 16 | import os |
17 | import optparse | 17 | import optparse |
18 | import re | ||
18 | import sys | 19 | import sys |
19 | 20 | ||
20 | from error import NoSuchProjectError | 21 | from error import NoSuchProjectError |
22 | from error import InvalidProjectGroupsError | ||
21 | 23 | ||
22 | class Command(object): | 24 | class Command(object): |
23 | """Base class for any command line action in repo. | 25 | """Base class for any command line action in repo. |
@@ -63,9 +65,16 @@ class Command(object): | |||
63 | all = self.manifest.projects | 65 | all = self.manifest.projects |
64 | result = [] | 66 | result = [] |
65 | 67 | ||
68 | mp = self.manifest.manifestProject | ||
69 | |||
70 | groups = mp.config.GetString('manifest.groups') | ||
71 | if groups: | ||
72 | groups = re.split('[,\s]+', groups) | ||
73 | |||
66 | if not args: | 74 | if not args: |
67 | for project in all.values(): | 75 | for project in all.values(): |
68 | if missing_ok or project.Exists: | 76 | if ((missing_ok or project.Exists) and |
77 | project.MatchesGroups(groups)): | ||
69 | result.append(project) | 78 | result.append(project) |
70 | else: | 79 | else: |
71 | by_path = None | 80 | by_path = None |
@@ -102,6 +111,8 @@ class Command(object): | |||
102 | raise NoSuchProjectError(arg) | 111 | raise NoSuchProjectError(arg) |
103 | if not missing_ok and not project.Exists: | 112 | if not missing_ok and not project.Exists: |
104 | raise NoSuchProjectError(arg) | 113 | raise NoSuchProjectError(arg) |
114 | if not project.MatchesGroups(groups): | ||
115 | raise InvalidProjectGroupsError(arg) | ||
105 | 116 | ||
106 | result.append(project) | 117 | result.append(project) |
107 | 118 | ||
diff --git a/docs/manifest-format.txt b/docs/manifest-format.txt index 21f19db6..a7bb1561 100644 --- a/docs/manifest-format.txt +++ b/docs/manifest-format.txt | |||
@@ -48,6 +48,7 @@ following DTD: | |||
48 | <!ATTLIST project path CDATA #IMPLIED> | 48 | <!ATTLIST project path CDATA #IMPLIED> |
49 | <!ATTLIST project remote IDREF #IMPLIED> | 49 | <!ATTLIST project remote IDREF #IMPLIED> |
50 | <!ATTLIST project revision CDATA #IMPLIED> | 50 | <!ATTLIST project revision CDATA #IMPLIED> |
51 | <!ATTLIST project groups CDATA #IMPLIED> | ||
51 | 52 | ||
52 | <!ELEMENT remove-project (EMPTY)> | 53 | <!ELEMENT remove-project (EMPTY)> |
53 | <!ATTLIST remove-project name CDATA #REQUIRED> | 54 | <!ATTLIST remove-project name CDATA #REQUIRED> |
@@ -158,6 +159,10 @@ Tags and/or explicit SHA-1s should work in theory, but have not | |||
158 | been extensively tested. If not supplied the revision given by | 159 | been extensively tested. If not supplied the revision given by |
159 | the default element is used. | 160 | the default element is used. |
160 | 161 | ||
162 | Attribute `groups`: List of groups to which this project belongs, | ||
163 | whitespace or comma separated. All projects are part of the group | ||
164 | "default" unless "-default" is specified in the list of groups. | ||
165 | |||
161 | Element remove-project | 166 | Element remove-project |
162 | ---------------------- | 167 | ---------------------- |
163 | 168 | ||
@@ -77,6 +77,18 @@ class NoSuchProjectError(Exception): | |||
77 | return 'in current directory' | 77 | return 'in current directory' |
78 | return self.name | 78 | return self.name |
79 | 79 | ||
80 | |||
81 | class InvalidProjectGroupsError(Exception): | ||
82 | """A specified project is not suitable for the specified groups | ||
83 | """ | ||
84 | def __init__(self, name=None): | ||
85 | self.name = name | ||
86 | |||
87 | def __str__(self): | ||
88 | if self.Name is None: | ||
89 | return 'in current directory' | ||
90 | return self.name | ||
91 | |||
80 | class RepoChangedException(Exception): | 92 | class RepoChangedException(Exception): |
81 | """Thrown if 'repo sync' results in repo updating its internal | 93 | """Thrown if 'repo sync' results in repo updating its internal |
82 | repo or manifest repositories. In this special case we must | 94 | repo or manifest repositories. In this special case we must |
diff --git a/manifest_xml.py b/manifest_xml.py index 44538690..a250382f 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
@@ -119,6 +119,12 @@ class XmlManifest(object): | |||
119 | def Save(self, fd, peg_rev=False): | 119 | def Save(self, fd, peg_rev=False): |
120 | """Write the current manifest out to the given file descriptor. | 120 | """Write the current manifest out to the given file descriptor. |
121 | """ | 121 | """ |
122 | mp = self.manifestProject | ||
123 | |||
124 | groups = mp.config.GetString('manifest.groups') | ||
125 | if groups: | ||
126 | groups = re.split('[,\s]+', groups) | ||
127 | |||
122 | doc = xml.dom.minidom.Document() | 128 | doc = xml.dom.minidom.Document() |
123 | root = doc.createElement('manifest') | 129 | root = doc.createElement('manifest') |
124 | doc.appendChild(root) | 130 | doc.appendChild(root) |
@@ -167,6 +173,10 @@ class XmlManifest(object): | |||
167 | 173 | ||
168 | for p in sort_projects: | 174 | for p in sort_projects: |
169 | p = self.projects[p] | 175 | p = self.projects[p] |
176 | |||
177 | if not p.MatchesGroups(groups): | ||
178 | continue | ||
179 | |||
170 | e = doc.createElement('project') | 180 | e = doc.createElement('project') |
171 | root.appendChild(e) | 181 | root.appendChild(e) |
172 | e.setAttribute('name', p.name) | 182 | e.setAttribute('name', p.name) |
@@ -190,6 +200,9 @@ class XmlManifest(object): | |||
190 | ce.setAttribute('dest', c.dest) | 200 | ce.setAttribute('dest', c.dest) |
191 | e.appendChild(ce) | 201 | e.appendChild(ce) |
192 | 202 | ||
203 | if p.groups: | ||
204 | e.setAttribute('groups', ','.join(p.groups)) | ||
205 | |||
193 | if self._repo_hooks_project: | 206 | if self._repo_hooks_project: |
194 | root.appendChild(doc.createTextNode('')) | 207 | root.appendChild(doc.createTextNode('')) |
195 | e = doc.createElement('repo-hooks') | 208 | e = doc.createElement('repo-hooks') |
@@ -504,6 +517,12 @@ class XmlManifest(object): | |||
504 | else: | 517 | else: |
505 | rebase = rebase.lower() in ("yes", "true", "1") | 518 | rebase = rebase.lower() in ("yes", "true", "1") |
506 | 519 | ||
520 | groups = node.getAttribute('groups') | ||
521 | if groups: | ||
522 | groups = re.split('[,\s]+', groups) | ||
523 | else: | ||
524 | groups = None | ||
525 | |||
507 | if self.IsMirror: | 526 | if self.IsMirror: |
508 | relpath = None | 527 | relpath = None |
509 | worktree = None | 528 | worktree = None |
@@ -520,7 +539,8 @@ class XmlManifest(object): | |||
520 | relpath = path, | 539 | relpath = path, |
521 | revisionExpr = revisionExpr, | 540 | revisionExpr = revisionExpr, |
522 | revisionId = None, | 541 | revisionId = None, |
523 | rebase = rebase) | 542 | rebase = rebase, |
543 | groups = groups) | ||
524 | 544 | ||
525 | for n in node.childNodes: | 545 | for n in node.childNodes: |
526 | if n.nodeName == 'copyfile': | 546 | if n.nodeName == 'copyfile': |
@@ -504,7 +504,8 @@ class Project(object): | |||
504 | relpath, | 504 | relpath, |
505 | revisionExpr, | 505 | revisionExpr, |
506 | revisionId, | 506 | revisionId, |
507 | rebase = True): | 507 | rebase = True, |
508 | groups = None): | ||
508 | self.manifest = manifest | 509 | self.manifest = manifest |
509 | self.name = name | 510 | self.name = name |
510 | self.remote = remote | 511 | self.remote = remote |
@@ -524,6 +525,7 @@ class Project(object): | |||
524 | self.revisionId = revisionId | 525 | self.revisionId = revisionId |
525 | 526 | ||
526 | self.rebase = rebase | 527 | self.rebase = rebase |
528 | self.groups = groups | ||
527 | 529 | ||
528 | self.snapshots = {} | 530 | self.snapshots = {} |
529 | self.copyfiles = [] | 531 | self.copyfiles = [] |
@@ -645,6 +647,45 @@ class Project(object): | |||
645 | 647 | ||
646 | return heads | 648 | return heads |
647 | 649 | ||
650 | def MatchesGroups(self, manifest_groups): | ||
651 | """Returns true if the manifest groups specified at init should cause | ||
652 | this project to be synced. | ||
653 | Prefixing a manifest group with "-" inverts the meaning of a group. | ||
654 | All projects are implicitly labelled with "default" unless they are | ||
655 | explicitly labelled "-default". | ||
656 | If any non-inverted manifest groups are specified, the default label | ||
657 | is ignored. | ||
658 | Specifying only inverted groups implies "default". | ||
659 | """ | ||
660 | project_groups = self.groups | ||
661 | if not manifest_groups: | ||
662 | return not project_groups or not "-default" in project_groups | ||
663 | |||
664 | if not project_groups: | ||
665 | project_groups = ["default"] | ||
666 | elif not ("default" in project_groups or "-default" in project_groups): | ||
667 | project_groups.append("default") | ||
668 | |||
669 | plus_groups = [x for x in manifest_groups if not x.startswith("-")] | ||
670 | minus_groups = [x[1:] for x in manifest_groups if x.startswith("-")] | ||
671 | |||
672 | if not plus_groups: | ||
673 | plus_groups.append("default") | ||
674 | |||
675 | for group in minus_groups: | ||
676 | if group in project_groups: | ||
677 | # project was excluded by -group | ||
678 | return False | ||
679 | |||
680 | for group in plus_groups: | ||
681 | if group in project_groups: | ||
682 | # project was included by group | ||
683 | return True | ||
684 | |||
685 | # groups were specified that did not include this project | ||
686 | if plus_groups: | ||
687 | return False | ||
688 | return True | ||
648 | 689 | ||
649 | ## Status Display ## | 690 | ## Status Display ## |
650 | 691 | ||
@@ -2091,7 +2132,8 @@ class MetaProject(Project): | |||
2091 | remote = RemoteSpec('origin'), | 2132 | remote = RemoteSpec('origin'), |
2092 | relpath = '.repo/%s' % name, | 2133 | relpath = '.repo/%s' % name, |
2093 | revisionExpr = 'refs/heads/master', | 2134 | revisionExpr = 'refs/heads/master', |
2094 | revisionId = None) | 2135 | revisionId = None, |
2136 | groups = None) | ||
2095 | 2137 | ||
2096 | def PreSync(self): | 2138 | def PreSync(self): |
2097 | if self.Exists: | 2139 | if self.Exists: |
@@ -28,7 +28,7 @@ if __name__ == '__main__': | |||
28 | del magic | 28 | del magic |
29 | 29 | ||
30 | # increment this whenever we make important changes to this script | 30 | # increment this whenever we make important changes to this script |
31 | VERSION = (1, 14) | 31 | VERSION = (1, 15) |
32 | 32 | ||
33 | # increment this if the MAINTAINER_KEYS block is modified | 33 | # increment this if the MAINTAINER_KEYS block is modified |
34 | KEYRING_VERSION = (1,0) | 34 | KEYRING_VERSION = (1,0) |
@@ -125,6 +125,10 @@ group.add_option('--reference', | |||
125 | group.add_option('--depth', type='int', default=None, | 125 | group.add_option('--depth', type='int', default=None, |
126 | dest='depth', | 126 | dest='depth', |
127 | help='create a shallow clone with given depth; see git clone') | 127 | help='create a shallow clone with given depth; see git clone') |
128 | group.add_option('-g', '--groups', | ||
129 | dest='groups', default="", | ||
130 | help='restrict manifest projects to ones with a specified group', | ||
131 | metavar='GROUP') | ||
128 | 132 | ||
129 | 133 | ||
130 | # Tool | 134 | # Tool |
diff --git a/subcmds/init.py b/subcmds/init.py index 1cba3665..6cf39d14 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
@@ -86,6 +86,10 @@ to update the working directory files. | |||
86 | g.add_option('--depth', type='int', default=None, | 86 | g.add_option('--depth', type='int', default=None, |
87 | dest='depth', | 87 | dest='depth', |
88 | help='create a shallow clone with given depth; see git clone') | 88 | help='create a shallow clone with given depth; see git clone') |
89 | g.add_option('-g', '--groups', | ||
90 | dest='groups', default="", | ||
91 | help='restrict manifest projects to ones with a specified group', | ||
92 | metavar='GROUP') | ||
89 | 93 | ||
90 | # Tool | 94 | # Tool |
91 | g = p.add_option_group('repo Version options') | 95 | g = p.add_option_group('repo Version options') |
@@ -135,6 +139,8 @@ to update the working directory files. | |||
135 | r.ResetFetch() | 139 | r.ResetFetch() |
136 | r.Save() | 140 | r.Save() |
137 | 141 | ||
142 | m.config.SetString('manifest.groups', opt.groups) | ||
143 | |||
138 | if opt.reference: | 144 | if opt.reference: |
139 | m.config.SetString('repo.reference', opt.reference) | 145 | m.config.SetString('repo.reference', opt.reference) |
140 | 146 | ||
diff --git a/subcmds/sync.py b/subcmds/sync.py index 74b3f183..63227afd 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -277,7 +277,7 @@ later is required to fix a server side protocol bug. | |||
277 | 277 | ||
278 | def UpdateProjectList(self): | 278 | def UpdateProjectList(self): |
279 | new_project_paths = [] | 279 | new_project_paths = [] |
280 | for project in self.manifest.projects.values(): | 280 | for project in self.GetProjects(None, missing_ok=True): |
281 | if project.relpath: | 281 | if project.relpath: |
282 | new_project_paths.append(project.relpath) | 282 | new_project_paths.append(project.relpath) |
283 | file_name = 'project.list' | 283 | file_name = 'project.list' |
@@ -306,7 +306,8 @@ later is required to fix a server side protocol bug. | |||
306 | worktree = os.path.join(self.manifest.topdir, path), | 306 | worktree = os.path.join(self.manifest.topdir, path), |
307 | relpath = path, | 307 | relpath = path, |
308 | revisionExpr = 'HEAD', | 308 | revisionExpr = 'HEAD', |
309 | revisionId = None) | 309 | revisionId = None, |
310 | groups = None) | ||
310 | 311 | ||
311 | if project.IsDirty(): | 312 | if project.IsDirty(): |
312 | print >>sys.stderr, 'error: Cannot remove project "%s": \ | 313 | print >>sys.stderr, 'error: Cannot remove project "%s": \ |