Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Provides a short mapping of all the branches in your local repo, organized | 6 """Provides a short mapping of all the branches in your local repo, organized |
| 7 by their upstream ('tracking branch') layout. | 7 by their upstream ('tracking branch') layout. |
| 8 | 8 |
| 9 Example: | 9 Example: |
| 10 origin/master | 10 origin/master |
| 11 cool_feature | 11 cool_feature |
| 12 dependent_feature | 12 dependent_feature |
| 13 other_dependent_feature | 13 other_dependent_feature |
| 14 other_feature | 14 other_feature |
| 15 | 15 |
| 16 Branches are colorized as follows: | 16 Branches are colorized as follows: |
| 17 * Red - a remote branch (usually the root of all local branches) | 17 * Red - a remote branch (usually the root of all local branches) |
| 18 * Cyan - a local branch which is the same as HEAD | 18 * Cyan - a local branch which is the same as HEAD |
| 19 * Note that multiple branches may be Cyan, if they are all on the same | 19 * Note that multiple branches may be Cyan, if they are all on the same |
| 20 commit, and you have that commit checked out. | 20 commit, and you have that commit checked out. |
| 21 * Green - a local branch | 21 * Green - a local branch |
| 22 * Magenta - a tag | 22 * Magenta - a tag |
| 23 * Magenta '{NO UPSTREAM}' - If you have local branches which do not track any | 23 * Magenta '{NO UPSTREAM}' - If you have local branches which do not track any |
| 24 upstream, then you will see this. | 24 upstream, then you will see this. |
| 25 """ | 25 """ |
| 26 | 26 |
| 27 import argparse | 27 import argparse |
| 28 import collections | 28 import collections |
| 29 import sys | 29 import sys |
| 30 import subprocess2 | |
| 30 | 31 |
| 31 from third_party import colorama | 32 from third_party import colorama |
| 32 from third_party.colorama import Fore, Style | 33 from third_party.colorama import Fore, Style |
| 33 | 34 |
| 34 from git_common import current_branch, upstream, tags, get_branches_info | 35 from git_common import current_branch, upstream, tags, get_branches_info |
| 35 from git_common import get_git_version, MIN_UPSTREAM_TRACK_GIT_VERSION, hash_one | 36 from git_common import get_git_version, MIN_UPSTREAM_TRACK_GIT_VERSION, hash_one |
| 36 | 37 |
| 37 DEFAULT_SEPARATOR = ' ' * 4 | 38 DEFAULT_SEPARATOR = ' ' * 4 |
| 38 | 39 |
| 39 | 40 |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 119 self.__branches_info = get_branches_info( | 120 self.__branches_info = get_branches_info( |
| 120 include_tracking_status=self.verbosity >= 1) | 121 include_tracking_status=self.verbosity >= 1) |
| 121 roots = set() | 122 roots = set() |
| 122 | 123 |
| 123 # A map of parents to a list of their children. | 124 # A map of parents to a list of their children. |
| 124 for branch, branch_info in self.__branches_info.iteritems(): | 125 for branch, branch_info in self.__branches_info.iteritems(): |
| 125 if not branch_info: | 126 if not branch_info: |
| 126 continue | 127 continue |
| 127 | 128 |
| 128 parent = branch_info.upstream | 129 parent = branch_info.upstream |
| 129 if parent and not self.__branches_info[parent]: | 130 if not self.__branches_info[parent]: |
| 130 branch_upstream = upstream(branch) | 131 branch_upstream = upstream(branch) |
| 131 # If git can't find the upstream, mark the upstream as gone. | 132 # If git can't find the upstream, mark the upstream as gone. |
| 132 if branch_upstream: | 133 if branch_upstream: |
| 133 parent = branch_upstream | 134 parent = branch_upstream |
| 134 else: | 135 else: |
| 135 self.__gone_branches.add(parent) | 136 self.__gone_branches.add(parent) |
| 136 # A parent that isn't in the branches info is a root. | 137 # A parent that isn't in the branches info is a root. |
| 137 roots.add(parent) | 138 roots.add(parent) |
| 138 | 139 |
| 139 self.__parent_map[parent].append(branch) | 140 self.__parent_map[parent].append(branch) |
| 140 | 141 |
| 141 self.__current_branch = current_branch() | 142 self.__current_branch = current_branch() |
| 142 self.__current_hash = hash_one('HEAD', short=True) | 143 self.__current_hash = hash_one('HEAD', short=True) |
| 143 self.__tag_set = tags() | 144 self.__tag_set = tags() |
| 144 | 145 |
| 145 if roots: | 146 if roots: |
| 146 for root in sorted(roots): | 147 for root in sorted(roots): |
| 147 self.__append_branch(root) | 148 self.__append_branch(root) |
| 148 else: | 149 else: |
| 149 no_branches = OutputLine() | 150 no_branches = OutputLine() |
| 150 no_branches.append('No User Branches') | 151 no_branches.append('No User Branches') |
| 151 self.output.append(no_branches) | 152 self.output.append(no_branches) |
| 152 | 153 |
| 153 def __is_invalid_parent(self, parent): | 154 def __is_invalid_parent(self, parent): |
| 154 return not parent or parent in self.__gone_branches | 155 return not parent or parent in self.__gone_branches |
| 155 | 156 |
| 156 def __color_for_branch(self, branch, branch_hash): | 157 def __color_for_branch(self, branch, branch_hash): |
| 157 if branch.startswith('origin'): | 158 if branch.startswith('origin'): |
| 158 color = Fore.RED | 159 color = Fore.RED |
| 160 elif branch.startswith('branch-heads'): | |
| 161 color = Fore.BLUE | |
|
iannucci
2014/09/23 00:42:01
could you regenerate the manpage to show this, too
calamity
2014/09/23 03:28:01
Done.
| |
| 159 elif self.__is_invalid_parent(branch) or branch in self.__tag_set: | 162 elif self.__is_invalid_parent(branch) or branch in self.__tag_set: |
| 160 color = Fore.MAGENTA | 163 color = Fore.MAGENTA |
| 161 elif self.__current_hash.startswith(branch_hash): | 164 elif self.__current_hash.startswith(branch_hash): |
| 162 color = Fore.CYAN | 165 color = Fore.CYAN |
| 163 else: | 166 else: |
| 164 color = Fore.GREEN | 167 color = Fore.GREEN |
| 165 | 168 |
| 166 if self.__current_hash.startswith(branch_hash): | 169 if branch_hash and self.__current_hash.startswith(branch_hash): |
| 167 color += Style.BRIGHT | 170 color += Style.BRIGHT |
| 168 else: | 171 else: |
| 169 color += Style.NORMAL | 172 color += Style.NORMAL |
| 170 | 173 |
| 171 return color | 174 return color |
| 172 | 175 |
| 173 def __append_branch(self, branch, depth=0): | 176 def __append_branch(self, branch, depth=0): |
| 174 """Recurses through the tree structure and appends an OutputLine to the | 177 """Recurses through the tree structure and appends an OutputLine to the |
| 175 OutputManager for each branch.""" | 178 OutputManager for each branch.""" |
| 176 branch_info = self.__branches_info[branch] | 179 branch_info = self.__branches_info[branch] |
| 177 if branch_info: | 180 if branch_info: |
| 178 branch_hash = branch_info.hash | 181 branch_hash = branch_info.hash |
| 179 else: | 182 else: |
| 180 branch_hash = hash_one(branch, short=True) | 183 try: |
| 184 branch_hash = hash_one(branch, short=True) | |
| 185 except subprocess2.CalledProcessError: | |
| 186 branch_hash = None | |
| 181 | 187 |
| 182 line = OutputLine() | 188 line = OutputLine() |
| 183 | 189 |
| 184 # The branch name with appropriate indentation. | 190 # The branch name with appropriate indentation. |
| 185 suffix = '' | 191 suffix = '' |
| 186 if branch == self.__current_branch or ( | 192 if branch == self.__current_branch or ( |
| 187 self.__current_branch == 'HEAD' and branch == self.__current_hash): | 193 self.__current_branch == 'HEAD' and branch == self.__current_hash): |
| 188 suffix = ' *' | 194 suffix = ' *' |
| 189 branch_string = branch | 195 branch_string = branch |
| 190 if branch in self.__gone_branches: | 196 if branch in self.__gone_branches: |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 226 line.append(front_separator, separator=' ') | 232 line.append(front_separator, separator=' ') |
| 227 line.append(ahead_string, separator=' ', color=Fore.MAGENTA) | 233 line.append(ahead_string, separator=' ', color=Fore.MAGENTA) |
| 228 line.append(center_separator, separator=' ') | 234 line.append(center_separator, separator=' ') |
| 229 line.append(behind_string, separator=' ', color=Fore.MAGENTA) | 235 line.append(behind_string, separator=' ', color=Fore.MAGENTA) |
| 230 line.append(back_separator) | 236 line.append(back_separator) |
| 231 | 237 |
| 232 # The Rietveld issue associated with the branch. | 238 # The Rietveld issue associated with the branch. |
| 233 if self.verbosity >= 2: | 239 if self.verbosity >= 2: |
| 234 import git_cl # avoid heavy import cost unless we need it | 240 import git_cl # avoid heavy import cost unless we need it |
| 235 none_text = '' if self.__is_invalid_parent(branch) else 'None' | 241 none_text = '' if self.__is_invalid_parent(branch) else 'None' |
| 236 url = git_cl.Changelist(branchref=branch).GetIssueURL() | 242 url = git_cl.Changelist( |
| 243 branchref=branch_hash).GetIssueURL() if branch_hash else None | |
| 237 line.append(url or none_text, color=Fore.BLUE if url else Fore.WHITE) | 244 line.append(url or none_text, color=Fore.BLUE if url else Fore.WHITE) |
| 238 | 245 |
| 239 self.output.append(line) | 246 self.output.append(line) |
| 240 | 247 |
| 241 for child in sorted(self.__parent_map.pop(branch, ())): | 248 for child in sorted(self.__parent_map.pop(branch, ())): |
| 242 self.__append_branch(child, depth=depth + 1) | 249 self.__append_branch(child, depth=depth + 1) |
| 243 | 250 |
| 244 | 251 |
| 245 def main(argv): | 252 def main(argv): |
| 246 colorama.init() | 253 colorama.init() |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 261 opts = parser.parse_args(argv[1:]) | 268 opts = parser.parse_args(argv[1:]) |
| 262 | 269 |
| 263 mapper = BranchMapper() | 270 mapper = BranchMapper() |
| 264 mapper.verbosity = opts.v | 271 mapper.verbosity = opts.v |
| 265 mapper.output.nocolor = opts.nocolor | 272 mapper.output.nocolor = opts.nocolor |
| 266 mapper.start() | 273 mapper.start() |
| 267 print mapper.output.as_formatted_string() | 274 print mapper.output.as_formatted_string() |
| 268 | 275 |
| 269 if __name__ == '__main__': | 276 if __name__ == '__main__': |
| 270 sys.exit(main(sys.argv)) | 277 sys.exit(main(sys.argv)) |
| OLD | NEW |