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