From ea2e330e43c182dc16b0111ebc69ee5a71ee4ce1 Mon Sep 17 00:00:00 2001 From: Gavin Mak Date: Sat, 11 Mar 2023 06:46:20 +0000 Subject: Format codebase with black and check formatting in CQ Apply rules set by https://gerrit-review.googlesource.com/c/git-repo/+/362954/ across the codebase and fix any lingering errors caught by flake8. Also check black formatting in run_tests (and CQ). Bug: b/267675342 Change-Id: I972d77649dac351150dcfeb1cd1ad0ea2efc1956 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/363474 Reviewed-by: Mike Frysinger Tested-by: Gavin Mak Commit-Queue: Gavin Mak --- manifest_xml.py | 4107 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 2178 insertions(+), 1929 deletions(-) (limited to 'manifest_xml.py') diff --git a/manifest_xml.py b/manifest_xml.py index 5b83f368..9603906f 100644 --- a/manifest_xml.py +++ b/manifest_xml.py @@ -26,415 +26,452 @@ from git_config import GitConfig from git_refs import R_HEADS, HEAD from git_superproject import Superproject import platform_utils -from project import (Annotation, RemoteSpec, Project, RepoProject, - ManifestProject) -from error import (ManifestParseError, ManifestInvalidPathError, - ManifestInvalidRevisionError) +from project import ( + Annotation, + RemoteSpec, + Project, + RepoProject, + ManifestProject, +) +from error import ( + ManifestParseError, + ManifestInvalidPathError, + ManifestInvalidRevisionError, +) from wrapper import Wrapper -MANIFEST_FILE_NAME = 'manifest.xml' -LOCAL_MANIFEST_NAME = 'local_manifest.xml' -LOCAL_MANIFESTS_DIR_NAME = 'local_manifests' -SUBMANIFEST_DIR = 'submanifests' +MANIFEST_FILE_NAME = "manifest.xml" +LOCAL_MANIFEST_NAME = "local_manifest.xml" +LOCAL_MANIFESTS_DIR_NAME = "local_manifests" +SUBMANIFEST_DIR = "submanifests" # Limit submanifests to an arbitrary depth for loop detection. MAX_SUBMANIFEST_DEPTH = 8 # Add all projects from sub manifest into a group. -SUBMANIFEST_GROUP_PREFIX = 'submanifest:' +SUBMANIFEST_GROUP_PREFIX = "submanifest:" # Add all projects from local manifest into a group. -LOCAL_MANIFEST_GROUP_PREFIX = 'local:' +LOCAL_MANIFEST_GROUP_PREFIX = "local:" # ContactInfo has the self-registered bug url, supplied by the manifest authors. -ContactInfo = collections.namedtuple('ContactInfo', 'bugurl') +ContactInfo = collections.namedtuple("ContactInfo", "bugurl") # urljoin gets confused if the scheme is not known. -urllib.parse.uses_relative.extend([ - 'ssh', - 'git', - 'persistent-https', - 'sso', - 'rpc']) -urllib.parse.uses_netloc.extend([ - 'ssh', - 'git', - 'persistent-https', - 'sso', - 'rpc']) +urllib.parse.uses_relative.extend( + ["ssh", "git", "persistent-https", "sso", "rpc"] +) +urllib.parse.uses_netloc.extend( + ["ssh", "git", "persistent-https", "sso", "rpc"] +) def XmlBool(node, attr, default=None): - """Determine boolean value of |node|'s |attr|. - - Invalid values will issue a non-fatal warning. - - Args: - node: XML node whose attributes we access. - attr: The attribute to access. - default: If the attribute is not set (value is empty), then use this. - - Returns: - True if the attribute is a valid string representing true. - False if the attribute is a valid string representing false. - |default| otherwise. - """ - value = node.getAttribute(attr) - s = value.lower() - if s == '': - return default - elif s in {'yes', 'true', '1'}: - return True - elif s in {'no', 'false', '0'}: - return False - else: - print('warning: manifest: %s="%s": ignoring invalid XML boolean' % - (attr, value), file=sys.stderr) - return default + """Determine boolean value of |node|'s |attr|. + + Invalid values will issue a non-fatal warning. + + Args: + node: XML node whose attributes we access. + attr: The attribute to access. + default: If the attribute is not set (value is empty), then use this. + + Returns: + True if the attribute is a valid string representing true. + False if the attribute is a valid string representing false. + |default| otherwise. + """ + value = node.getAttribute(attr) + s = value.lower() + if s == "": + return default + elif s in {"yes", "true", "1"}: + return True + elif s in {"no", "false", "0"}: + return False + else: + print( + 'warning: manifest: %s="%s": ignoring invalid XML boolean' + % (attr, value), + file=sys.stderr, + ) + return default def XmlInt(node, attr, default=None): - """Determine integer value of |node|'s |attr|. + """Determine integer value of |node|'s |attr|. - Args: - node: XML node whose attributes we access. - attr: The attribute to access. - default: If the attribute is not set (value is empty), then use this. + Args: + node: XML node whose attributes we access. + attr: The attribute to access. + default: If the attribute is not set (value is empty), then use this. - Returns: - The number if the attribute is a valid number. + Returns: + The number if the attribute is a valid number. - Raises: - ManifestParseError: The number is invalid. - """ - value = node.getAttribute(attr) - if not value: - return default + Raises: + ManifestParseError: The number is invalid. + """ + value = node.getAttribute(attr) + if not value: + return default - try: - return int(value) - except ValueError: - raise ManifestParseError('manifest: invalid %s="%s" integer' % - (attr, value)) + try: + return int(value) + except ValueError: + raise ManifestParseError( + 'manifest: invalid %s="%s" integer' % (attr, value) + ) class _Default(object): - """Project defaults within the manifest.""" + """Project defaults within the manifest.""" - revisionExpr = None - destBranchExpr = None - upstreamExpr = None - remote = None - sync_j = None - sync_c = False - sync_s = False - sync_tags = True + revisionExpr = None + destBranchExpr = None + upstreamExpr = None + remote = None + sync_j = None + sync_c = False + sync_s = False + sync_tags = True - def __eq__(self, other): - if not isinstance(other, _Default): - return False - return self.__dict__ == other.__dict__ + def __eq__(self, other): + if not isinstance(other, _Default): + return False + return self.__dict__ == other.__dict__ - def __ne__(self, other): - if not isinstance(other, _Default): - return True - return self.__dict__ != other.__dict__ + def __ne__(self, other): + if not isinstance(other, _Default): + return True + return self.__dict__ != other.__dict__ class _XmlRemote(object): - def __init__(self, - name, - alias=None, - fetch=None, - pushUrl=None, - manifestUrl=None, - review=None, - revision=None): - self.name = name - self.fetchUrl = fetch - self.pushUrl = pushUrl - self.manifestUrl = manifestUrl - self.remoteAlias = alias - self.reviewUrl = review - self.revision = revision - self.resolvedFetchUrl = self._resolveFetchUrl() - self.annotations = [] - - def __eq__(self, other): - if not isinstance(other, _XmlRemote): - return False - return (sorted(self.annotations) == sorted(other.annotations) and - self.name == other.name and self.fetchUrl == other.fetchUrl and - self.pushUrl == other.pushUrl and self.remoteAlias == other.remoteAlias - and self.reviewUrl == other.reviewUrl and self.revision == other.revision) - - def __ne__(self, other): - return not self.__eq__(other) - - def _resolveFetchUrl(self): - if self.fetchUrl is None: - return '' - url = self.fetchUrl.rstrip('/') - manifestUrl = self.manifestUrl.rstrip('/') - # urljoin will gets confused over quite a few things. The ones we care - # about here are: - # * no scheme in the base url, like - # We handle no scheme by replacing it with an obscure protocol, gopher - # and then replacing it with the original when we are done. - - if manifestUrl.find(':') != manifestUrl.find('/') - 1: - url = urllib.parse.urljoin('gopher://' + manifestUrl, url) - url = re.sub(r'^gopher://', '', url) - else: - url = urllib.parse.urljoin(manifestUrl, url) - return url - - def ToRemoteSpec(self, projectName): - fetchUrl = self.resolvedFetchUrl.rstrip('/') - url = fetchUrl + '/' + projectName - remoteName = self.name - if self.remoteAlias: - remoteName = self.remoteAlias - return RemoteSpec(remoteName, - url=url, - pushUrl=self.pushUrl, - review=self.reviewUrl, - orig_name=self.name, - fetchUrl=self.fetchUrl) - - def AddAnnotation(self, name, value, keep): - self.annotations.append(Annotation(name, value, keep)) + def __init__( + self, + name, + alias=None, + fetch=None, + pushUrl=None, + manifestUrl=None, + review=None, + revision=None, + ): + self.name = name + self.fetchUrl = fetch + self.pushUrl = pushUrl + self.manifestUrl = manifestUrl + self.remoteAlias = alias + self.reviewUrl = review + self.revision = revision + self.resolvedFetchUrl = self._resolveFetchUrl() + self.annotations = [] + + def __eq__(self, other): + if not isinstance(other, _XmlRemote): + return False + return ( + sorted(self.annotations) == sorted(other.annotations) + and self.name == other.name + and self.fetchUrl == other.fetchUrl + and self.pushUrl == other.pushUrl + and self.remoteAlias == other.remoteAlias + and self.reviewUrl == other.reviewUrl + and self.revision == other.revision + ) + + def __ne__(self, other): + return not self.__eq__(other) + + def _resolveFetchUrl(self): + if self.fetchUrl is None: + return "" + url = self.fetchUrl.rstrip("/") + manifestUrl = self.manifestUrl.rstrip("/") + # urljoin will gets confused over quite a few things. The ones we care + # about here are: + # * no scheme in the base url, like + # We handle no scheme by replacing it with an obscure protocol, gopher + # and then replacing it with the original when we are done. + + if manifestUrl.find(":") != manifestUrl.find("/") - 1: + url = urllib.parse.urljoin("gopher://" + manifestUrl, url) + url = re.sub(r"^gopher://", "", url) + else: + url = urllib.parse.urljoin(manifestUrl, url) + return url + + def ToRemoteSpec(self, projectName): + fetchUrl = self.resolvedFetchUrl.rstrip("/") + url = fetchUrl + "/" + projectName + remoteName = self.name + if self.remoteAlias: + remoteName = self.remoteAlias + return RemoteSpec( + remoteName, + url=url, + pushUrl=self.pushUrl, + review=self.reviewUrl, + orig_name=self.name, + fetchUrl=self.fetchUrl, + ) + + def AddAnnotation(self, name, value, keep): + self.annotations.append(Annotation(name, value, keep)) class _XmlSubmanifest: - """Manage the element specified in the manifest. - - Attributes: - name: a string, the name for this submanifest. - remote: a string, the remote.name for this submanifest. - project: a string, the name of the manifest project. - revision: a string, the commitish. - manifestName: a string, the submanifest file name. - groups: a list of strings, the groups to add to all projects in the submanifest. - default_groups: a list of strings, the default groups to sync. - path: a string, the relative path for the submanifest checkout. - parent: an XmlManifest, the parent manifest. - annotations: (derived) a list of annotations. - present: (derived) a boolean, whether the sub manifest file is present. - """ - def __init__(self, - name, - remote=None, - project=None, - revision=None, - manifestName=None, - groups=None, - default_groups=None, - path=None, - parent=None): - self.name = name - self.remote = remote - self.project = project - self.revision = revision - self.manifestName = manifestName - self.groups = groups - self.default_groups = default_groups - self.path = path - self.parent = parent - self.annotations = [] - outer_client = parent._outer_client or parent - if self.remote and not self.project: - raise ManifestParseError( - f'Submanifest {name}: must specify project when remote is given.') - # Construct the absolute path to the manifest file using the parent's - # method, so that we can correctly create our repo_client. - manifestFile = parent.SubmanifestInfoDir( - os.path.join(parent.path_prefix, self.relpath), - os.path.join('manifests', manifestName or 'default.xml')) - linkFile = parent.SubmanifestInfoDir( - os.path.join(parent.path_prefix, self.relpath), MANIFEST_FILE_NAME) - rc = self.repo_client = RepoClient( - parent.repodir, linkFile, parent_groups=','.join(groups) or '', - submanifest_path=self.relpath, outer_client=outer_client, - default_groups=default_groups) - - self.present = os.path.exists(manifestFile) - - def __eq__(self, other): - if not isinstance(other, _XmlSubmanifest): - return False - return ( - self.name == other.name and - self.remote == other.remote and - self.project == other.project and - self.revision == other.revision and - self.manifestName == other.manifestName and - self.groups == other.groups and - self.default_groups == other.default_groups and - self.path == other.path and - sorted(self.annotations) == sorted(other.annotations)) - - def __ne__(self, other): - return not self.__eq__(other) - - def ToSubmanifestSpec(self): - """Return a SubmanifestSpec object, populating attributes""" - mp = self.parent.manifestProject - remote = self.parent.remotes[self.remote or self.parent.default.remote.name] - # If a project was given, generate the url from the remote and project. - # If not, use this manifestProject's url. - if self.project: - manifestUrl = remote.ToRemoteSpec(self.project).url - else: - manifestUrl = mp.GetRemote().url - manifestName = self.manifestName or 'default.xml' - revision = self.revision or self.name - path = self.path or revision.split('/')[-1] - groups = self.groups or [] - default_groups = self.default_groups or [] + """Manage the element specified in the manifest. + + Attributes: + name: a string, the name for this submanifest. + remote: a string, the remote.name for this submanifest. + project: a string, the name of the manifest project. + revision: a string, the commitish. + manifestName: a string, the submanifest file name. + groups: a list of strings, the groups to add to all projects in the + submanifest. + default_groups: a list of strings, the default groups to sync. + path: a string, the relative path for the submanifest checkout. + parent: an XmlManifest, the parent manifest. + annotations: (derived) a list of annotations. + present: (derived) a boolean, whether the sub manifest file is present. + """ - return SubmanifestSpec(self.name, manifestUrl, manifestName, revision, path, - groups) + def __init__( + self, + name, + remote=None, + project=None, + revision=None, + manifestName=None, + groups=None, + default_groups=None, + path=None, + parent=None, + ): + self.name = name + self.remote = remote + self.project = project + self.revision = revision + self.manifestName = manifestName + self.groups = groups + self.default_groups = default_groups + self.path = path + self.parent = parent + self.annotations = [] + outer_client = parent._outer_client or parent + if self.remote and not self.project: + raise ManifestParseError( + f"Submanifest {name}: must specify project when remote is " + "given." + ) + # Construct the absolute path to the manifest file using the parent's + # method, so that we can correctly create our repo_client. + manifestFile = parent.SubmanifestInfoDir( + os.path.join(parent.path_prefix, self.relpath), + os.path.join("manifests", manifestName or "default.xml"), + ) + linkFile = parent.SubmanifestInfoDir( + os.path.join(parent.path_prefix, self.relpath), MANIFEST_FILE_NAME + ) + self.repo_client = RepoClient( + parent.repodir, + linkFile, + parent_groups=",".join(groups) or "", + submanifest_path=self.relpath, + outer_client=outer_client, + default_groups=default_groups, + ) + + self.present = os.path.exists(manifestFile) + + def __eq__(self, other): + if not isinstance(other, _XmlSubmanifest): + return False + return ( + self.name == other.name + and self.remote == other.remote + and self.project == other.project + and self.revision == other.revision + and self.manifestName == other.manifestName + and self.groups == other.groups + and self.default_groups == other.default_groups + and self.path == other.path + and sorted(self.annotations) == sorted(other.annotations) + ) + + def __ne__(self, other): + return not self.__eq__(other) + + def ToSubmanifestSpec(self): + """Return a SubmanifestSpec object, populating attributes""" + mp = self.parent.manifestProject + remote = self.parent.remotes[ + self.remote or self.parent.default.remote.name + ] + # If a project was given, generate the url from the remote and project. + # If not, use this manifestProject's url. + if self.project: + manifestUrl = remote.ToRemoteSpec(self.project).url + else: + manifestUrl = mp.GetRemote().url + manifestName = self.manifestName or "default.xml" + revision = self.revision or self.name + path = self.path or revision.split("/")[-1] + groups = self.groups or [] - @property - def relpath(self): - """The path of this submanifest relative to the parent manifest.""" - revision = self.revision or self.name - return self.path or revision.split('/')[-1] + return SubmanifestSpec( + self.name, manifestUrl, manifestName, revision, path, groups + ) - def GetGroupsStr(self): - """Returns the `groups` given for this submanifest.""" - if self.groups: - return ','.join(self.groups) - return '' + @property + def relpath(self): + """The path of this submanifest relative to the parent manifest.""" + revision = self.revision or self.name + return self.path or revision.split("/")[-1] - def GetDefaultGroupsStr(self): - """Returns the `default-groups` given for this submanifest.""" - return ','.join(self.default_groups or []) + def GetGroupsStr(self): + """Returns the `groups` given for this submanifest.""" + if self.groups: + return ",".join(self.groups) + return "" - def AddAnnotation(self, name, value, keep): - """Add annotations to the submanifest.""" - self.annotations.append(Annotation(name, value, keep)) + def GetDefaultGroupsStr(self): + """Returns the `default-groups` given for this submanifest.""" + return ",".join(self.default_groups or []) + def AddAnnotation(self, name, value, keep): + """Add annotations to the submanifest.""" + self.annotations.append(Annotation(name, value, keep)) -class SubmanifestSpec: - """The submanifest element, with all fields expanded.""" - - def __init__(self, - name, - manifestUrl, - manifestName, - revision, - path, - groups): - self.name = name - self.manifestUrl = manifestUrl - self.manifestName = manifestName - self.revision = revision - self.path = path - self.groups = groups or [] +class SubmanifestSpec: + """The submanifest element, with all fields expanded.""" -class XmlManifest(object): - """manages the repo configuration file""" + def __init__(self, name, manifestUrl, manifestName, revision, path, groups): + self.name = name + self.manifestUrl = manifestUrl + self.manifestName = manifestName + self.revision = revision + self.path = path + self.groups = groups or [] - def __init__(self, repodir, manifest_file, local_manifests=None, - outer_client=None, parent_groups='', submanifest_path='', - default_groups=None): - """Initialize. - Args: - repodir: Path to the .repo/ dir for holding all internal checkout state. - It must be in the top directory of the repo client checkout. - manifest_file: Full path to the manifest file to parse. This will usually - be |repodir|/|MANIFEST_FILE_NAME|. - local_manifests: Full path to the directory of local override manifests. - This will usually be |repodir|/|LOCAL_MANIFESTS_DIR_NAME|. - outer_client: RepoClient of the outer manifest. - parent_groups: a string, the groups to apply to this projects. - submanifest_path: The submanifest root relative to the repo root. - default_groups: a string, the default manifest groups to use. - """ - # TODO(vapier): Move this out of this class. - self.globalConfig = GitConfig.ForUser() - - self.repodir = os.path.abspath(repodir) - self._CheckLocalPath(submanifest_path) - self.topdir = os.path.dirname(self.repodir) - if submanifest_path: - # This avoids a trailing os.path.sep when submanifest_path is empty. - self.topdir = os.path.join(self.topdir, submanifest_path) - if manifest_file != os.path.abspath(manifest_file): - raise ManifestParseError('manifest_file must be abspath') - self.manifestFile = manifest_file - if not outer_client or outer_client == self: - # manifestFileOverrides only exists in the outer_client's manifest, since - # that is the only instance left when Unload() is called on the outer - # manifest. - self.manifestFileOverrides = {} - self.local_manifests = local_manifests - self._load_local_manifests = True - self.parent_groups = parent_groups - self.default_groups = default_groups - - if outer_client and self.isGitcClient: - raise ManifestParseError('Multi-manifest is incompatible with `gitc-init`') - - if submanifest_path and not outer_client: - # If passing a submanifest_path, there must be an outer_client. - raise ManifestParseError(f'Bad call to {self.__class__.__name__}') - - # If self._outer_client is None, this is not a checkout that supports - # multi-tree. - self._outer_client = outer_client or self - - self.repoProject = RepoProject(self, 'repo', - gitdir=os.path.join(repodir, 'repo/.git'), - worktree=os.path.join(repodir, 'repo')) - - mp = self.SubmanifestProject(self.path_prefix) - self.manifestProject = mp - - # This is a bit hacky, but we're in a chicken & egg situation: all the - # normal repo settings live in the manifestProject which we just setup - # above, so we couldn't easily query before that. We assume Project() - # init doesn't care if this changes afterwards. - if os.path.exists(mp.gitdir) and mp.use_worktree: - mp.use_git_worktrees = True - - self.Unload() - - def Override(self, name, load_local_manifests=True): - """Use a different manifest, just for the current instantiation. - """ - path = None - - # Look for a manifest by path in the filesystem (including the cwd). - if not load_local_manifests: - local_path = os.path.abspath(name) - if os.path.isfile(local_path): - path = local_path - - # Look for manifests by name from the manifests repo. - if path is None: - path = os.path.join(self.manifestProject.worktree, name) - if not os.path.isfile(path): - raise ManifestParseError('manifest %s not found' % name) - - self._load_local_manifests = load_local_manifests - self._outer_client.manifestFileOverrides[self.path_prefix] = path - self.Unload() - self._Load() - - def Link(self, name): - """Update the repo metadata to use a different manifest. - """ - self.Override(name) - - # Old versions of repo would generate symlinks we need to clean up. - platform_utils.remove(self.manifestFile, missing_ok=True) - # This file is interpreted as if it existed inside the manifest repo. - # That allows us to use with the relative file name. - with open(self.manifestFile, 'w') as fp: - fp.write(""" +class XmlManifest(object): + """manages the repo configuration file""" + + def __init__( + self, + repodir, + manifest_file, + local_manifests=None, + outer_client=None, + parent_groups="", + submanifest_path="", + default_groups=None, + ): + """Initialize. + + Args: + repodir: Path to the .repo/ dir for holding all internal checkout + state. It must be in the top directory of the repo client + checkout. + manifest_file: Full path to the manifest file to parse. This will + usually be |repodir|/|MANIFEST_FILE_NAME|. + local_manifests: Full path to the directory of local override + manifests. This will usually be + |repodir|/|LOCAL_MANIFESTS_DIR_NAME|. + outer_client: RepoClient of the outer manifest. + parent_groups: a string, the groups to apply to this projects. + submanifest_path: The submanifest root relative to the repo root. + default_groups: a string, the default manifest groups to use. + """ + # TODO(vapier): Move this out of this class. + self.globalConfig = GitConfig.ForUser() + + self.repodir = os.path.abspath(repodir) + self._CheckLocalPath(submanifest_path) + self.topdir = os.path.dirname(self.repodir) + if submanifest_path: + # This avoids a trailing os.path.sep when submanifest_path is empty. + self.topdir = os.path.join(self.topdir, submanifest_path) + if manifest_file != os.path.abspath(manifest_file): + raise ManifestParseError("manifest_file must be abspath") + self.manifestFile = manifest_file + if not outer_client or outer_client == self: + # manifestFileOverrides only exists in the outer_client's manifest, + # since that is the only instance left when Unload() is called on + # the outer manifest. + self.manifestFileOverrides = {} + self.local_manifests = local_manifests + self._load_local_manifests = True + self.parent_groups = parent_groups + self.default_groups = default_groups + + if outer_client and self.isGitcClient: + raise ManifestParseError( + "Multi-manifest is incompatible with `gitc-init`" + ) + + if submanifest_path and not outer_client: + # If passing a submanifest_path, there must be an outer_client. + raise ManifestParseError(f"Bad call to {self.__class__.__name__}") + + # If self._outer_client is None, this is not a checkout that supports + # multi-tree. + self._outer_client = outer_client or self + + self.repoProject = RepoProject( + self, + "repo", + gitdir=os.path.join(repodir, "repo/.git"), + worktree=os.path.join(repodir, "repo"), + ) + + mp = self.SubmanifestProject(self.path_prefix) + self.manifestProject = mp + + # This is a bit hacky, but we're in a chicken & egg situation: all the + # normal repo settings live in the manifestProject which we just setup + # above, so we couldn't easily query before that. We assume Project() + # init doesn't care if this changes afterwards. + if os.path.exists(mp.gitdir) and mp.use_worktree: + mp.use_git_worktrees = True + + self.Unload() + + def Override(self, name, load_local_manifests=True): + """Use a different manifest, just for the current instantiation.""" + path = None + + # Look for a manifest by path in the filesystem (including the cwd). + if not load_local_manifests: + local_path = os.path.abspath(name) + if os.path.isfile(local_path): + path = local_path + + # Look for manifests by name from the manifests repo. + if path is None: + path = os.path.join(self.manifestProject.worktree, name) + if not os.path.isfile(path): + raise ManifestParseError("manifest %s not found" % name) + + self._load_local_manifests = load_local_manifests + self._outer_client.manifestFileOverrides[self.path_prefix] = path + self.Unload() + self._Load() + + def Link(self, name): + """Update the repo metadata to use a different manifest.""" + self.Override(name) + + # Old versions of repo would generate symlinks we need to clean up. + platform_utils.remove(self.manifestFile, missing_ok=True) + # This file is interpreted as if it existed inside the manifest repo. + # That allows us to use with the relative file name. + with open(self.manifestFile, "w") as fp: + fp.write( + """