diff options
Diffstat (limited to 'manifest_xml.py')
-rw-r--r-- | manifest_xml.py | 189 |
1 files changed, 144 insertions, 45 deletions
diff --git a/manifest_xml.py b/manifest_xml.py index 0c2b45e5..68ead53c 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
@@ -12,6 +12,7 @@ | |||
12 | # See the License for the specific language governing permissions and | 12 | # See the License for the specific language governing permissions and |
13 | # limitations under the License. | 13 | # limitations under the License. |
14 | 14 | ||
15 | import collections | ||
15 | import itertools | 16 | import itertools |
16 | import os | 17 | import os |
17 | import platform | 18 | import platform |
@@ -24,14 +25,21 @@ import gitc_utils | |||
24 | from git_config import GitConfig, IsId | 25 | from git_config import GitConfig, IsId |
25 | from git_refs import R_HEADS, HEAD | 26 | from git_refs import R_HEADS, HEAD |
26 | import platform_utils | 27 | import platform_utils |
27 | from project import RemoteSpec, Project, MetaProject | 28 | from project import Annotation, RemoteSpec, Project, MetaProject |
28 | from error import (ManifestParseError, ManifestInvalidPathError, | 29 | from error import (ManifestParseError, ManifestInvalidPathError, |
29 | ManifestInvalidRevisionError) | 30 | ManifestInvalidRevisionError) |
31 | from wrapper import Wrapper | ||
30 | 32 | ||
31 | MANIFEST_FILE_NAME = 'manifest.xml' | 33 | MANIFEST_FILE_NAME = 'manifest.xml' |
32 | LOCAL_MANIFEST_NAME = 'local_manifest.xml' | 34 | LOCAL_MANIFEST_NAME = 'local_manifest.xml' |
33 | LOCAL_MANIFESTS_DIR_NAME = 'local_manifests' | 35 | LOCAL_MANIFESTS_DIR_NAME = 'local_manifests' |
34 | 36 | ||
37 | # Add all projects from local manifest into a group. | ||
38 | LOCAL_MANIFEST_GROUP_PREFIX = 'local:' | ||
39 | |||
40 | # ContactInfo has the self-registered bug url, supplied by the manifest authors. | ||
41 | ContactInfo = collections.namedtuple('ContactInfo', 'bugurl') | ||
42 | |||
35 | # urljoin gets confused if the scheme is not known. | 43 | # urljoin gets confused if the scheme is not known. |
36 | urllib.parse.uses_relative.extend([ | 44 | urllib.parse.uses_relative.extend([ |
37 | 'ssh', | 45 | 'ssh', |
@@ -114,9 +122,13 @@ class _Default(object): | |||
114 | sync_tags = True | 122 | sync_tags = True |
115 | 123 | ||
116 | def __eq__(self, other): | 124 | def __eq__(self, other): |
125 | if not isinstance(other, _Default): | ||
126 | return False | ||
117 | return self.__dict__ == other.__dict__ | 127 | return self.__dict__ == other.__dict__ |
118 | 128 | ||
119 | def __ne__(self, other): | 129 | def __ne__(self, other): |
130 | if not isinstance(other, _Default): | ||
131 | return True | ||
120 | return self.__dict__ != other.__dict__ | 132 | return self.__dict__ != other.__dict__ |
121 | 133 | ||
122 | 134 | ||
@@ -137,14 +149,22 @@ class _XmlRemote(object): | |||
137 | self.reviewUrl = review | 149 | self.reviewUrl = review |
138 | self.revision = revision | 150 | self.revision = revision |
139 | self.resolvedFetchUrl = self._resolveFetchUrl() | 151 | self.resolvedFetchUrl = self._resolveFetchUrl() |
152 | self.annotations = [] | ||
140 | 153 | ||
141 | def __eq__(self, other): | 154 | def __eq__(self, other): |
142 | return self.__dict__ == other.__dict__ | 155 | if not isinstance(other, _XmlRemote): |
156 | return False | ||
157 | return (sorted(self.annotations) == sorted(other.annotations) and | ||
158 | self.name == other.name and self.fetchUrl == other.fetchUrl and | ||
159 | self.pushUrl == other.pushUrl and self.remoteAlias == other.remoteAlias | ||
160 | and self.reviewUrl == other.reviewUrl and self.revision == other.revision) | ||
143 | 161 | ||
144 | def __ne__(self, other): | 162 | def __ne__(self, other): |
145 | return self.__dict__ != other.__dict__ | 163 | return not self.__eq__(other) |
146 | 164 | ||
147 | def _resolveFetchUrl(self): | 165 | def _resolveFetchUrl(self): |
166 | if self.fetchUrl is None: | ||
167 | return '' | ||
148 | url = self.fetchUrl.rstrip('/') | 168 | url = self.fetchUrl.rstrip('/') |
149 | manifestUrl = self.manifestUrl.rstrip('/') | 169 | manifestUrl = self.manifestUrl.rstrip('/') |
150 | # urljoin will gets confused over quite a few things. The ones we care | 170 | # urljoin will gets confused over quite a few things. The ones we care |
@@ -173,6 +193,9 @@ class _XmlRemote(object): | |||
173 | orig_name=self.name, | 193 | orig_name=self.name, |
174 | fetchUrl=self.fetchUrl) | 194 | fetchUrl=self.fetchUrl) |
175 | 195 | ||
196 | def AddAnnotation(self, name, value, keep): | ||
197 | self.annotations.append(Annotation(name, value, keep)) | ||
198 | |||
176 | 199 | ||
177 | class XmlManifest(object): | 200 | class XmlManifest(object): |
178 | """manages the repo configuration file""" | 201 | """manages the repo configuration file""" |
@@ -247,8 +270,7 @@ class XmlManifest(object): | |||
247 | self.Override(name) | 270 | self.Override(name) |
248 | 271 | ||
249 | # Old versions of repo would generate symlinks we need to clean up. | 272 | # Old versions of repo would generate symlinks we need to clean up. |
250 | if os.path.lexists(self.manifestFile): | 273 | platform_utils.remove(self.manifestFile, missing_ok=True) |
251 | platform_utils.remove(self.manifestFile) | ||
252 | # This file is interpreted as if it existed inside the manifest repo. | 274 | # This file is interpreted as if it existed inside the manifest repo. |
253 | # That allows us to use <include> with the relative file name. | 275 | # That allows us to use <include> with the relative file name. |
254 | with open(self.manifestFile, 'w') as fp: | 276 | with open(self.manifestFile, 'w') as fp: |
@@ -282,6 +304,13 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
282 | if r.revision is not None: | 304 | if r.revision is not None: |
283 | e.setAttribute('revision', r.revision) | 305 | e.setAttribute('revision', r.revision) |
284 | 306 | ||
307 | for a in r.annotations: | ||
308 | if a.keep == 'true': | ||
309 | ae = doc.createElement('annotation') | ||
310 | ae.setAttribute('name', a.name) | ||
311 | ae.setAttribute('value', a.value) | ||
312 | e.appendChild(ae) | ||
313 | |||
285 | def _ParseList(self, field): | 314 | def _ParseList(self, field): |
286 | """Parse fields that contain flattened lists. | 315 | """Parse fields that contain flattened lists. |
287 | 316 | ||
@@ -477,6 +506,15 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
477 | if not d.remote or remote.orig_name != remoteName: | 506 | if not d.remote or remote.orig_name != remoteName: |
478 | remoteName = remote.orig_name | 507 | remoteName = remote.orig_name |
479 | e.setAttribute('remote', remoteName) | 508 | e.setAttribute('remote', remoteName) |
509 | revision = remote.revision or d.revisionExpr | ||
510 | if not revision or revision != self._superproject['revision']: | ||
511 | e.setAttribute('revision', self._superproject['revision']) | ||
512 | root.appendChild(e) | ||
513 | |||
514 | if self._contactinfo.bugurl != Wrapper().BUG_URL: | ||
515 | root.appendChild(doc.createTextNode('')) | ||
516 | e = doc.createElement('contactinfo') | ||
517 | e.setAttribute('bugurl', self._contactinfo.bugurl) | ||
480 | root.appendChild(e) | 518 | root.appendChild(e) |
481 | 519 | ||
482 | return doc | 520 | return doc |
@@ -490,6 +528,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
490 | 'manifest-server', | 528 | 'manifest-server', |
491 | 'repo-hooks', | 529 | 'repo-hooks', |
492 | 'superproject', | 530 | 'superproject', |
531 | 'contactinfo', | ||
493 | } | 532 | } |
494 | # Elements that may be repeated. | 533 | # Elements that may be repeated. |
495 | MULTI_ELEMENTS = { | 534 | MULTI_ELEMENTS = { |
@@ -566,6 +605,11 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
566 | return self._superproject | 605 | return self._superproject |
567 | 606 | ||
568 | @property | 607 | @property |
608 | def contactinfo(self): | ||
609 | self._Load() | ||
610 | return self._contactinfo | ||
611 | |||
612 | @property | ||
569 | def notice(self): | 613 | def notice(self): |
570 | self._Load() | 614 | self._Load() |
571 | return self._notice | 615 | return self._notice |
@@ -596,6 +640,17 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
596 | return set(x.strip() for x in exclude.split(',')) | 640 | return set(x.strip() for x in exclude.split(',')) |
597 | 641 | ||
598 | @property | 642 | @property |
643 | def UseLocalManifests(self): | ||
644 | return self._load_local_manifests | ||
645 | |||
646 | def SetUseLocalManifests(self, value): | ||
647 | self._load_local_manifests = value | ||
648 | |||
649 | @property | ||
650 | def HasLocalManifests(self): | ||
651 | return self._load_local_manifests and self.local_manifests | ||
652 | |||
653 | @property | ||
599 | def IsMirror(self): | 654 | def IsMirror(self): |
600 | return self.manifestProject.config.GetBoolean('repo.mirror') | 655 | return self.manifestProject.config.GetBoolean('repo.mirror') |
601 | 656 | ||
@@ -630,6 +685,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
630 | self._default = None | 685 | self._default = None |
631 | self._repo_hooks_project = None | 686 | self._repo_hooks_project = None |
632 | self._superproject = {} | 687 | self._superproject = {} |
688 | self._contactinfo = ContactInfo(Wrapper().BUG_URL) | ||
633 | self._notice = None | 689 | self._notice = None |
634 | self.branch = None | 690 | self.branch = None |
635 | self._manifest_server = None | 691 | self._manifest_server = None |
@@ -657,7 +713,9 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
657 | # Since local manifests are entirely managed by the user, allow | 713 | # Since local manifests are entirely managed by the user, allow |
658 | # them to point anywhere the user wants. | 714 | # them to point anywhere the user wants. |
659 | nodes.append(self._ParseManifestXml( | 715 | nodes.append(self._ParseManifestXml( |
660 | local, self.repodir, restrict_includes=False)) | 716 | local, self.repodir, |
717 | parent_groups=f'{LOCAL_MANIFEST_GROUP_PREFIX}:{local_file[:-4]}', | ||
718 | restrict_includes=False)) | ||
661 | except OSError: | 719 | except OSError: |
662 | pass | 720 | pass |
663 | 721 | ||
@@ -754,9 +812,10 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
754 | for node in itertools.chain(*node_list): | 812 | for node in itertools.chain(*node_list): |
755 | if node.nodeName == 'default': | 813 | if node.nodeName == 'default': |
756 | new_default = self._ParseDefault(node) | 814 | new_default = self._ParseDefault(node) |
815 | emptyDefault = not node.hasAttributes() and not node.hasChildNodes() | ||
757 | if self._default is None: | 816 | if self._default is None: |
758 | self._default = new_default | 817 | self._default = new_default |
759 | elif new_default != self._default: | 818 | elif not emptyDefault and new_default != self._default: |
760 | raise ManifestParseError('duplicate default in %s' % | 819 | raise ManifestParseError('duplicate default in %s' % |
761 | (self.manifestFile)) | 820 | (self.manifestFile)) |
762 | 821 | ||
@@ -795,6 +854,8 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
795 | for subproject in project.subprojects: | 854 | for subproject in project.subprojects: |
796 | recursively_add_projects(subproject) | 855 | recursively_add_projects(subproject) |
797 | 856 | ||
857 | repo_hooks_project = None | ||
858 | enabled_repo_hooks = None | ||
798 | for node in itertools.chain(*node_list): | 859 | for node in itertools.chain(*node_list): |
799 | if node.nodeName == 'project': | 860 | if node.nodeName == 'project': |
800 | project = self._ParseProject(node) | 861 | project = self._ParseProject(node) |
@@ -807,6 +868,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
807 | 'project: %s' % name) | 868 | 'project: %s' % name) |
808 | 869 | ||
809 | path = node.getAttribute('path') | 870 | path = node.getAttribute('path') |
871 | dest_path = node.getAttribute('dest-path') | ||
810 | groups = node.getAttribute('groups') | 872 | groups = node.getAttribute('groups') |
811 | if groups: | 873 | if groups: |
812 | groups = self._ParseList(groups) | 874 | groups = self._ParseList(groups) |
@@ -815,46 +877,37 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
815 | if remote: | 877 | if remote: |
816 | remote = self._get_remote(node) | 878 | remote = self._get_remote(node) |
817 | 879 | ||
880 | named_projects = self._projects[name] | ||
881 | if dest_path and not path and len(named_projects) > 1: | ||
882 | raise ManifestParseError('extend-project cannot use dest-path when ' | ||
883 | 'matching multiple projects: %s' % name) | ||
818 | for p in self._projects[name]: | 884 | for p in self._projects[name]: |
819 | if path and p.relpath != path: | 885 | if path and p.relpath != path: |
820 | continue | 886 | continue |
821 | if groups: | 887 | if groups: |
822 | p.groups.extend(groups) | 888 | p.groups.extend(groups) |
823 | if revision: | 889 | if revision: |
824 | p.revisionExpr = revision | 890 | p.SetRevision(revision) |
825 | if IsId(revision): | 891 | |
826 | p.revisionId = revision | ||
827 | else: | ||
828 | p.revisionId = None | ||
829 | if remote: | 892 | if remote: |
830 | p.remote = remote.ToRemoteSpec(name) | 893 | p.remote = remote.ToRemoteSpec(name) |
831 | if node.nodeName == 'repo-hooks': | ||
832 | # Get the name of the project and the (space-separated) list of enabled. | ||
833 | repo_hooks_project = self._reqatt(node, 'in-project') | ||
834 | enabled_repo_hooks = self._ParseList(self._reqatt(node, 'enabled-list')) | ||
835 | 894 | ||
895 | if dest_path: | ||
896 | del self._paths[p.relpath] | ||
897 | relpath, worktree, gitdir, objdir, _ = self.GetProjectPaths(name, dest_path) | ||
898 | p.UpdatePaths(relpath, worktree, gitdir, objdir) | ||
899 | self._paths[p.relpath] = p | ||
900 | |||
901 | if node.nodeName == 'repo-hooks': | ||
836 | # Only one project can be the hooks project | 902 | # Only one project can be the hooks project |
837 | if self._repo_hooks_project is not None: | 903 | if repo_hooks_project is not None: |
838 | raise ManifestParseError( | 904 | raise ManifestParseError( |
839 | 'duplicate repo-hooks in %s' % | 905 | 'duplicate repo-hooks in %s' % |
840 | (self.manifestFile)) | 906 | (self.manifestFile)) |
841 | 907 | ||
842 | # Store a reference to the Project. | 908 | # Get the name of the project and the (space-separated) list of enabled. |
843 | try: | 909 | repo_hooks_project = self._reqatt(node, 'in-project') |
844 | repo_hooks_projects = self._projects[repo_hooks_project] | 910 | enabled_repo_hooks = self._ParseList(self._reqatt(node, 'enabled-list')) |
845 | except KeyError: | ||
846 | raise ManifestParseError( | ||
847 | 'project %s not found for repo-hooks' % | ||
848 | (repo_hooks_project)) | ||
849 | |||
850 | if len(repo_hooks_projects) != 1: | ||
851 | raise ManifestParseError( | ||
852 | 'internal error parsing repo-hooks in %s' % | ||
853 | (self.manifestFile)) | ||
854 | self._repo_hooks_project = repo_hooks_projects[0] | ||
855 | |||
856 | # Store the enabled hooks in the Project object. | ||
857 | self._repo_hooks_project.enabled_repo_hooks = enabled_repo_hooks | ||
858 | if node.nodeName == 'superproject': | 911 | if node.nodeName == 'superproject': |
859 | name = self._reqatt(node, 'name') | 912 | name = self._reqatt(node, 'name') |
860 | # There can only be one superproject. | 913 | # There can only be one superproject. |
@@ -872,21 +925,51 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
872 | raise ManifestParseError("no remote for superproject %s within %s" % | 925 | raise ManifestParseError("no remote for superproject %s within %s" % |
873 | (name, self.manifestFile)) | 926 | (name, self.manifestFile)) |
874 | self._superproject['remote'] = remote.ToRemoteSpec(name) | 927 | self._superproject['remote'] = remote.ToRemoteSpec(name) |
928 | revision = node.getAttribute('revision') or remote.revision | ||
929 | if not revision: | ||
930 | revision = self._default.revisionExpr | ||
931 | if not revision: | ||
932 | raise ManifestParseError('no revision for superproject %s within %s' % | ||
933 | (name, self.manifestFile)) | ||
934 | self._superproject['revision'] = revision | ||
935 | if node.nodeName == 'contactinfo': | ||
936 | bugurl = self._reqatt(node, 'bugurl') | ||
937 | # This element can be repeated, later entries will clobber earlier ones. | ||
938 | self._contactinfo = ContactInfo(bugurl) | ||
939 | |||
875 | if node.nodeName == 'remove-project': | 940 | if node.nodeName == 'remove-project': |
876 | name = self._reqatt(node, 'name') | 941 | name = self._reqatt(node, 'name') |
877 | 942 | ||
878 | if name not in self._projects: | 943 | if name in self._projects: |
944 | for p in self._projects[name]: | ||
945 | del self._paths[p.relpath] | ||
946 | del self._projects[name] | ||
947 | |||
948 | # If the manifest removes the hooks project, treat it as if it deleted | ||
949 | # the repo-hooks element too. | ||
950 | if repo_hooks_project == name: | ||
951 | repo_hooks_project = None | ||
952 | elif not XmlBool(node, 'optional', False): | ||
879 | raise ManifestParseError('remove-project element specifies non-existent ' | 953 | raise ManifestParseError('remove-project element specifies non-existent ' |
880 | 'project: %s' % name) | 954 | 'project: %s' % name) |
881 | 955 | ||
882 | for p in self._projects[name]: | 956 | # Store repo hooks project information. |
883 | del self._paths[p.relpath] | 957 | if repo_hooks_project: |
884 | del self._projects[name] | 958 | # Store a reference to the Project. |
959 | try: | ||
960 | repo_hooks_projects = self._projects[repo_hooks_project] | ||
961 | except KeyError: | ||
962 | raise ManifestParseError( | ||
963 | 'project %s not found for repo-hooks' % | ||
964 | (repo_hooks_project)) | ||
885 | 965 | ||
886 | # If the manifest removes the hooks project, treat it as if it deleted | 966 | if len(repo_hooks_projects) != 1: |
887 | # the repo-hooks element too. | 967 | raise ManifestParseError( |
888 | if self._repo_hooks_project and (self._repo_hooks_project.name == name): | 968 | 'internal error parsing repo-hooks in %s' % |
889 | self._repo_hooks_project = None | 969 | (self.manifestFile)) |
970 | self._repo_hooks_project = repo_hooks_projects[0] | ||
971 | # Store the enabled hooks in the Project object. | ||
972 | self._repo_hooks_project.enabled_repo_hooks = enabled_repo_hooks | ||
890 | 973 | ||
891 | def _AddMetaProjectMirror(self, m): | 974 | def _AddMetaProjectMirror(self, m): |
892 | name = None | 975 | name = None |
@@ -945,7 +1028,14 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
945 | if revision == '': | 1028 | if revision == '': |
946 | revision = None | 1029 | revision = None |
947 | manifestUrl = self.manifestProject.config.GetString('remote.origin.url') | 1030 | manifestUrl = self.manifestProject.config.GetString('remote.origin.url') |
948 | return _XmlRemote(name, alias, fetch, pushUrl, manifestUrl, review, revision) | 1031 | |
1032 | remote = _XmlRemote(name, alias, fetch, pushUrl, manifestUrl, review, revision) | ||
1033 | |||
1034 | for n in node.childNodes: | ||
1035 | if n.nodeName == 'annotation': | ||
1036 | self._ParseAnnotation(remote, n) | ||
1037 | |||
1038 | return remote | ||
949 | 1039 | ||
950 | def _ParseDefault(self, node): | 1040 | def _ParseDefault(self, node): |
951 | """ | 1041 | """ |
@@ -1199,6 +1289,8 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
1199 | if '~' in path: | 1289 | if '~' in path: |
1200 | return '~ not allowed (due to 8.3 filenames on Windows filesystems)' | 1290 | return '~ not allowed (due to 8.3 filenames on Windows filesystems)' |
1201 | 1291 | ||
1292 | path_codepoints = set(path) | ||
1293 | |||
1202 | # Some filesystems (like Apple's HFS+) try to normalize Unicode codepoints | 1294 | # Some filesystems (like Apple's HFS+) try to normalize Unicode codepoints |
1203 | # which means there are alternative names for ".git". Reject paths with | 1295 | # which means there are alternative names for ".git". Reject paths with |
1204 | # these in it as there shouldn't be any reasonable need for them here. | 1296 | # these in it as there shouldn't be any reasonable need for them here. |
@@ -1222,10 +1314,17 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
1222 | u'\u206F', # NOMINAL DIGIT SHAPES | 1314 | u'\u206F', # NOMINAL DIGIT SHAPES |
1223 | u'\uFEFF', # ZERO WIDTH NO-BREAK SPACE | 1315 | u'\uFEFF', # ZERO WIDTH NO-BREAK SPACE |
1224 | } | 1316 | } |
1225 | if BAD_CODEPOINTS & set(path): | 1317 | if BAD_CODEPOINTS & path_codepoints: |
1226 | # This message is more expansive than reality, but should be fine. | 1318 | # This message is more expansive than reality, but should be fine. |
1227 | return 'Unicode combining characters not allowed' | 1319 | return 'Unicode combining characters not allowed' |
1228 | 1320 | ||
1321 | # Reject newlines as there shouldn't be any legitmate use for them, they'll | ||
1322 | # be confusing to users, and they can easily break tools that expect to be | ||
1323 | # able to iterate over newline delimited lists. This even applies to our | ||
1324 | # own code like .repo/project.list. | ||
1325 | if {'\r', '\n'} & path_codepoints: | ||
1326 | return 'Newlines not allowed' | ||
1327 | |||
1229 | # Assume paths might be used on case-insensitive filesystems. | 1328 | # Assume paths might be used on case-insensitive filesystems. |
1230 | path = path.lower() | 1329 | path = path.lower() |
1231 | 1330 | ||
@@ -1303,7 +1402,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
1303 | self._ValidateFilePaths('linkfile', src, dest) | 1402 | self._ValidateFilePaths('linkfile', src, dest) |
1304 | project.AddLinkFile(src, dest, self.topdir) | 1403 | project.AddLinkFile(src, dest, self.topdir) |
1305 | 1404 | ||
1306 | def _ParseAnnotation(self, project, node): | 1405 | def _ParseAnnotation(self, element, node): |
1307 | name = self._reqatt(node, 'name') | 1406 | name = self._reqatt(node, 'name') |
1308 | value = self._reqatt(node, 'value') | 1407 | value = self._reqatt(node, 'value') |
1309 | try: | 1408 | try: |
@@ -1313,7 +1412,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
1313 | if keep != "true" and keep != "false": | 1412 | if keep != "true" and keep != "false": |
1314 | raise ManifestParseError('optional "keep" attribute must be ' | 1413 | raise ManifestParseError('optional "keep" attribute must be ' |
1315 | '"true" or "false"') | 1414 | '"true" or "false"') |
1316 | project.AddAnnotation(name, value, keep) | 1415 | element.AddAnnotation(name, value, keep) |
1317 | 1416 | ||
1318 | def _get_remote(self, node): | 1417 | def _get_remote(self, node): |
1319 | name = node.getAttribute('remote') | 1418 | name = node.getAttribute('remote') |