| # Copyright (C) 2009 The Android Open Source Project | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import itertools | |
| import sys | |
| from color import Coloring | |
| from command import Command | |
| from command import DEFAULT_LOCAL_JOBS | |
| class BranchColoring(Coloring): | |
| def __init__(self, config): | |
| Coloring.__init__(self, config, "branch") | |
| self.current = self.printer("current", fg="green") | |
| self.local = self.printer("local") | |
| self.notinproject = self.printer("notinproject", fg="red") | |
| class BranchInfo: | |
| def __init__(self, name): | |
| self.name = name | |
| self.current = 0 | |
| self.published = 0 | |
| self.published_equal = 0 | |
| self.projects = [] | |
| def add(self, b): | |
| if b.current: | |
| self.current += 1 | |
| if b.published: | |
| self.published += 1 | |
| if b.revision == b.published: | |
| self.published_equal += 1 | |
| self.projects.append(b) | |
| @property | |
| def IsCurrent(self): | |
| return self.current > 0 | |
| @property | |
| def IsSplitCurrent(self): | |
| return self.current != 0 and self.current != len(self.projects) | |
| @property | |
| def IsPublished(self): | |
| return self.published > 0 | |
| @property | |
| def IsPublishedEqual(self): | |
| return self.published_equal == len(self.projects) | |
| class Branches(Command): | |
| COMMON = True | |
| helpSummary = "View current topic branches" | |
| helpUsage = """ | |
| %prog [<project>...] | |
| Summarizes the currently available topic branches. | |
| # Branch Display | |
| The branch display output by this command is organized into four | |
| columns of information; for example: | |
| *P nocolor | in repo | |
| repo2 | | |
| The first column contains a * if the branch is the currently | |
| checked out branch in any of the specified projects, or a blank | |
| if no project has the branch checked out. | |
| The second column contains either blank, p or P, depending upon | |
| the upload status of the branch. | |
| (blank): branch not yet published by repo upload | |
| P: all commits were published by repo upload | |
| p: only some commits were published by repo upload | |
| The third column contains the branch name. | |
| The fourth column (after the | separator) lists the projects that | |
| the branch appears in, or does not appear in. If no project list | |
| is shown, then the branch appears in all projects. | |
| """ | |
| PARALLEL_JOBS = DEFAULT_LOCAL_JOBS | |
| @classmethod | |
| def _ExpandProjectToBranches(cls, project_idx): | |
| """Expands a project into a list of branch names & associated info. | |
| Args: | |
| project_idx: project.Project index | |
| Returns: | |
| List[Tuple[str, git_config.Branch, int]] | |
| """ | |
| branches = [] | |
| project = cls.get_parallel_context()["projects"][project_idx] | |
| for name, b in project.GetBranches().items(): | |
| branches.append((name, b, project_idx)) | |
| return branches | |
| def Execute(self, opt, args): | |
| projects = self.GetProjects( | |
| args, all_manifests=not opt.this_manifest_only | |
| ) | |
| out = BranchColoring(self.manifest.manifestProject.config) | |
| all_branches = {} | |
| project_cnt = len(projects) | |
| def _ProcessResults(_pool, _output, results): | |
| for name, b, project_idx in itertools.chain.from_iterable(results): | |
| b.project = projects[project_idx] | |
| if name not in all_branches: | |
| all_branches[name] = BranchInfo(name) | |
| all_branches[name].add(b) | |
| with self.ParallelContext(): | |
| self.get_parallel_context()["projects"] = projects | |
| self.ExecuteInParallel( | |
| opt.jobs, | |
| self._ExpandProjectToBranches, | |
| range(len(projects)), | |
| callback=_ProcessResults, | |
| ) | |
| names = sorted(all_branches) | |
| if not names: | |
| print(" (no branches)", file=sys.stderr) | |
| return | |
| width = 25 | |
| for name in names: | |
| if width < len(name): | |
| width = len(name) | |
| for name in names: | |
| i = all_branches[name] | |
| in_cnt = len(i.projects) | |
| if i.IsCurrent: | |
| current = "*" | |
| hdr = out.current | |
| else: | |
| current = " " | |
| hdr = out.local | |
| if i.IsPublishedEqual: | |
| published = "P" | |
| elif i.IsPublished: | |
| published = "p" | |
| else: | |
| published = " " | |
| # A branch name can contain a percent sign, so we need to escape it. | |
| # Escape after f-string formatting to properly account for leading | |
| # spaces. | |
| hdr(f"{current}{published} {name:{width}}".replace("%", "%%")) | |
| out.write(" |") | |
| _RelPath = lambda p: p.RelPath(local=opt.this_manifest_only) | |
| if in_cnt < project_cnt: | |
| fmt = out.write | |
| paths = [] | |
| non_cur_paths = [] | |
| if i.IsSplitCurrent or (in_cnt <= project_cnt - in_cnt): | |
| in_type = "in" | |
| for b in i.projects: | |
| relpath = _RelPath(b.project) | |
| if not i.IsSplitCurrent or b.current: | |
| paths.append(relpath) | |
| else: | |
| non_cur_paths.append(relpath) | |
| else: | |
| fmt = out.notinproject | |
| in_type = "not in" | |
| have = set() | |
| for b in i.projects: | |
| have.add(_RelPath(b.project)) | |
| for p in projects: | |
| if _RelPath(p) not in have: | |
| paths.append(_RelPath(p)) | |
| s = f" {in_type} {', '.join(paths)}" | |
| if not i.IsSplitCurrent and (width + 7 + len(s) < 80): | |
| fmt = out.current if i.IsCurrent else fmt | |
| fmt(s) | |
| else: | |
| fmt(" %s:" % in_type) | |
| fmt = out.current if i.IsCurrent else out.write | |
| for p in paths: | |
| out.nl() | |
| fmt(width * " " + " %s" % p) | |
| fmt = out.write | |
| for p in non_cur_paths: | |
| out.nl() | |
| fmt(width * " " + " %s" % p) | |
| else: | |
| out.write(" in all projects") | |
| out.nl() |