Josip Sokcevic | 4de5dea | 2022-03-23 21:15:14 +0000 | [diff] [blame^] | 1 | #!/usr/bin/env python3 |
iannucci@chromium.org | a112f03 | 2014-03-13 07:47:50 +0000 | [diff] [blame] | 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 |
| 4 | # found in the LICENSE file. |
| 5 | |
iannucci@chromium.org | 8bc9b5c | 2014-03-12 01:36:18 +0000 | [diff] [blame] | 6 | """ |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 7 | usage: git map [-h] [--help] [<args>] |
| 8 | |
anatoly techtonik | c878ba6 | 2017-04-03 06:08:38 +0300 | [diff] [blame] | 9 | Enhances `git log --graph` view with information on commit branches + tags that |
| 10 | point to them. Items are colorized as follows: |
| 11 | |
iannucci@chromium.org | 8bc9b5c | 2014-03-12 01:36:18 +0000 | [diff] [blame] | 12 | * Cyan - Currently checked out branch |
| 13 | * Green - Local branch |
| 14 | * Red - Remote branches |
| 15 | * Magenta - Tags |
iannucci@chromium.org | c050a5b | 2014-03-26 06:18:50 +0000 | [diff] [blame] | 16 | * White - Merge Base Markers |
iannucci@chromium.org | 8bc9b5c | 2014-03-12 01:36:18 +0000 | [diff] [blame] | 17 | * Blue background - The currently checked out commit |
| 18 | """ |
iannucci@chromium.org | c050a5b | 2014-03-26 06:18:50 +0000 | [diff] [blame] | 19 | |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 20 | from __future__ import unicode_literals |
| 21 | |
| 22 | import os |
iannucci@chromium.org | 8bc9b5c | 2014-03-12 01:36:18 +0000 | [diff] [blame] | 23 | import sys |
| 24 | |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 25 | import git_common |
| 26 | import setup_color |
iannucci@chromium.org | 8bc9b5c | 2014-03-12 01:36:18 +0000 | [diff] [blame] | 27 | import subprocess2 |
| 28 | |
iannucci@chromium.org | 8bc9b5c | 2014-03-12 01:36:18 +0000 | [diff] [blame] | 29 | from third_party import colorama |
| 30 | |
iannucci@chromium.org | 8bc9b5c | 2014-03-12 01:36:18 +0000 | [diff] [blame] | 31 | |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 32 | if sys.version_info.major == 2: |
| 33 | # On Python 3, BrokenPipeError is raised instead. |
Aravind Vasudevan | c5f0cbb | 2022-01-24 23:56:57 +0000 | [diff] [blame] | 34 | # pylint:disable=redefined-builtin |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 35 | BrokenPipeError = IOError |
iannucci@chromium.org | 8bc9b5c | 2014-03-12 01:36:18 +0000 | [diff] [blame] | 36 | |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 37 | |
iannucci@chromium.org | 8bc9b5c | 2014-03-12 01:36:18 +0000 | [diff] [blame] | 38 | RESET = colorama.Fore.RESET + colorama.Back.RESET + colorama.Style.RESET_ALL |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 39 | BRIGHT = colorama.Style.BRIGHT |
iannucci@chromium.org | 8bc9b5c | 2014-03-12 01:36:18 +0000 | [diff] [blame] | 40 | |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 41 | BLUE_BACK = colorama.Back.BLUE + BRIGHT |
| 42 | BRIGHT_RED = colorama.Fore.RED + BRIGHT |
| 43 | CYAN = colorama.Fore.CYAN + BRIGHT |
| 44 | GREEN = colorama.Fore.GREEN + BRIGHT |
| 45 | MAGENTA = colorama.Fore.MAGENTA + BRIGHT |
| 46 | RED = colorama.Fore.RED |
| 47 | WHITE = colorama.Fore.WHITE + BRIGHT |
| 48 | YELLOW = colorama.Fore.YELLOW |
iannucci@chromium.org | c050a5b | 2014-03-26 06:18:50 +0000 | [diff] [blame] | 49 | |
anatoly techtonik | c878ba6 | 2017-04-03 06:08:38 +0300 | [diff] [blame] | 50 | |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 51 | def _print_help(outbuf): |
anatoly techtonik | c878ba6 | 2017-04-03 06:08:38 +0300 | [diff] [blame] | 52 | names = { |
| 53 | 'Cyan': CYAN, |
| 54 | 'Green': GREEN, |
| 55 | 'Magenta': MAGENTA, |
| 56 | 'Red': RED, |
| 57 | 'White': WHITE, |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 58 | 'Blue background': BLUE_BACK, |
anatoly techtonik | c878ba6 | 2017-04-03 06:08:38 +0300 | [diff] [blame] | 59 | } |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 60 | msg = '' |
anatoly techtonik | c878ba6 | 2017-04-03 06:08:38 +0300 | [diff] [blame] | 61 | for line in __doc__.splitlines(): |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 62 | for name, color in names.items(): |
| 63 | if name in line: |
| 64 | msg += line.replace('* ' + name, color + '* ' + name + RESET) + '\n' |
anatoly techtonik | c878ba6 | 2017-04-03 06:08:38 +0300 | [diff] [blame] | 65 | break |
| 66 | else: |
| 67 | msg += line + '\n' |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 68 | outbuf.write(msg.encode('utf-8', 'replace')) |
anatoly techtonik | c878ba6 | 2017-04-03 06:08:38 +0300 | [diff] [blame] | 69 | |
| 70 | |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 71 | def _color_branch(branch, all_branches, all_tags, current): |
Aravind Vasudevan | c5f0cbb | 2022-01-24 23:56:57 +0000 | [diff] [blame] | 72 | if branch in (current, 'HEAD -> ' + current): |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 73 | color = CYAN |
| 74 | current = None |
| 75 | elif branch in all_branches: |
| 76 | color = GREEN |
| 77 | all_branches.remove(branch) |
| 78 | elif branch in all_tags: |
| 79 | color = MAGENTA |
| 80 | elif branch.startswith('tag: '): |
| 81 | color = MAGENTA |
| 82 | branch = branch[len('tag: '):] |
| 83 | else: |
| 84 | color = RED |
| 85 | return color + branch + RESET |
| 86 | |
| 87 | |
| 88 | def _color_branch_list(branch_list, all_branches, all_tags, current): |
| 89 | if not branch_list: |
| 90 | return '' |
| 91 | colored_branches = (GREEN + ', ').join( |
| 92 | _color_branch(branch, all_branches, all_tags, current) |
| 93 | for branch in branch_list if branch != 'HEAD') |
| 94 | return (GREEN + '(' + colored_branches + GREEN + ') ' + RESET) |
| 95 | |
| 96 | |
| 97 | def _parse_log_line(line): |
| 98 | graph, branch_list, commit_date, subject = ( |
| 99 | line.decode('utf-8', 'replace').strip().split('\x00')) |
| 100 | branch_list = [] if not branch_list else branch_list.split(', ') |
| 101 | commit = graph.split()[-1] |
| 102 | graph = graph[:-len(commit)] |
| 103 | return graph, commit, branch_list, commit_date, subject |
| 104 | |
| 105 | |
| 106 | def main(argv, outbuf): |
| 107 | if '-h' in argv or '--help' in argv: |
| 108 | _print_help(outbuf) |
anatoly techtonik | c878ba6 | 2017-04-03 06:08:38 +0300 | [diff] [blame] | 109 | return 0 |
| 110 | |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 111 | map_extra = git_common.get_config_list('depot_tools.map_extra') |
| 112 | cmd = [ |
| 113 | git_common.GIT_EXE, 'log', git_common.root(), |
| 114 | '--graph', '--branches', '--tags', '--color=always', '--date=short', |
| 115 | '--pretty=format:%H%x00%D%x00%cd%x00%s' |
| 116 | ] + map_extra + argv |
iannucci@chromium.org | 8bc9b5c | 2014-03-12 01:36:18 +0000 | [diff] [blame] | 117 | |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 118 | log_proc = subprocess2.Popen(cmd, stdout=subprocess2.PIPE, shell=False) |
| 119 | |
| 120 | current = git_common.current_branch() |
| 121 | all_tags = set(git_common.tags()) |
| 122 | all_branches = set(git_common.branches()) |
iannucci@chromium.org | 8bc9b5c | 2014-03-12 01:36:18 +0000 | [diff] [blame] | 123 | if current in all_branches: |
| 124 | all_branches.remove(current) |
iannucci@chromium.org | c050a5b | 2014-03-26 06:18:50 +0000 | [diff] [blame] | 125 | |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 126 | merge_base_map = {} |
| 127 | for branch in all_branches: |
| 128 | merge_base = git_common.get_or_create_merge_base(branch) |
| 129 | if merge_base: |
| 130 | merge_base_map.setdefault(merge_base, set()).add(branch) |
| 131 | |
| 132 | for merge_base, branches in merge_base_map.items(): |
| 133 | merge_base_map[merge_base] = ', '.join(branches) |
| 134 | |
| 135 | try: |
| 136 | for line in log_proc.stdout: |
| 137 | if b'\x00' not in line: |
| 138 | outbuf.write(line) |
| 139 | continue |
| 140 | |
| 141 | graph, commit, branch_list, commit_date, subject = _parse_log_line(line) |
| 142 | |
| 143 | if 'HEAD' in branch_list: |
| 144 | graph = graph.replace('*', BLUE_BACK + '*') |
| 145 | |
| 146 | line = '{graph}{commit}\t{branches}{date} ~ {subject}'.format( |
| 147 | graph=graph, |
| 148 | commit=BRIGHT_RED + commit[:10] + RESET, |
| 149 | branches=_color_branch_list( |
| 150 | branch_list, all_branches, all_tags, current), |
| 151 | date=YELLOW + commit_date + RESET, |
| 152 | subject=subject) |
| 153 | |
| 154 | if commit in merge_base_map: |
| 155 | line += ' <({})'.format(WHITE + merge_base_map[commit] + RESET) |
| 156 | |
| 157 | line += os.linesep |
| 158 | outbuf.write(line.encode('utf-8', 'replace')) |
| 159 | except (BrokenPipeError, KeyboardInterrupt): |
iannucci@chromium.org | 8bc9b5c | 2014-03-12 01:36:18 +0000 | [diff] [blame] | 160 | pass |
iannucci@chromium.org | 8bc9b5c | 2014-03-12 01:36:18 +0000 | [diff] [blame] | 161 | return 0 |
| 162 | |
| 163 | |
| 164 | if __name__ == '__main__': |
Edward Lemur | b800fde | 2020-01-10 23:04:44 +0000 | [diff] [blame] | 165 | setup_color.init() |
| 166 | with git_common.less() as less_input: |
| 167 | sys.exit(main(sys.argv[1:], less_input)) |