| # 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, 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(object): |
| 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 |
| |
| 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 in itertools.chain.from_iterable(results): |
| if name not in all_branches: |
| all_branches[name] = BranchInfo(name) |
| all_branches[name].add(b) |
| |
| self.ExecuteInParallel( |
| opt.jobs, |
| expand_project_to_branches, |
| 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 = ' ' |
| |
| hdr('%c%c %-*s' % (current, published, width, name)) |
| 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 = b.project.relpath |
| if not i.IsSplitCurrent or b.current: |
| paths.append(_RelPath(b.project)) |
| else: |
| non_cur_paths.append(_RelPath(b.project)) |
| 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 = ' %s %s' % (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() |
| |
| |
| def expand_project_to_branches(project): |
| """Expands a project into a list of branch names & associated information. |
| |
| Args: |
| project: project.Project |
| |
| Returns: |
| List[Tuple[str, git_config.Branch]] |
| """ |
| branches = [] |
| for name, b in project.GetBranches().items(): |
| b.project = project |
| branches.append((name, b)) |
| return branches |