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