[email protected] | 8bc9b5c | 2014-03-12 01:36:18 | [diff] [blame] | 1 | #!/usr/bin/env python |
[email protected] | a112f03 | 2014-03-13 07:47:50 | [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 | |
[email protected] | 8bc9b5c | 2014-03-12 01:36:18 | [diff] [blame] | 6 | """ |
anatoly techtonik | c878ba6 | 2017-04-03 03:08:38 | [diff] [blame] | 7 | Enhances `git log --graph` view with information on commit branches + tags that |
| 8 | point to them. Items are colorized as follows: |
| 9 | |
[email protected] | 8bc9b5c | 2014-03-12 01:36:18 | [diff] [blame] | 10 | * Cyan - Currently checked out branch |
| 11 | * Green - Local branch |
| 12 | * Red - Remote branches |
| 13 | * Magenta - Tags |
[email protected] | c050a5b | 2014-03-26 06:18:50 | [diff] [blame] | 14 | * White - Merge Base Markers |
[email protected] | 8bc9b5c | 2014-03-12 01:36:18 | [diff] [blame] | 15 | * Blue background - The currently checked out commit |
| 16 | """ |
[email protected] | c050a5b | 2014-03-26 06:18:50 | [diff] [blame] | 17 | |
[email protected] | 8bc9b5c | 2014-03-12 01:36:18 | [diff] [blame] | 18 | import sys |
| 19 | |
| 20 | import subprocess2 |
| 21 | |
agable | 7aa2ddd | 2016-06-21 14:47:00 | [diff] [blame] | 22 | from git_common import current_branch, branches, tags, get_config_list, GIT_EXE |
[email protected] | 6332b15 | 2014-05-15 03:16:10 | [diff] [blame] | 23 | from git_common import get_or_create_merge_base, root |
[email protected] | 8bc9b5c | 2014-03-12 01:36:18 | [diff] [blame] | 24 | |
| 25 | from third_party import colorama |
| 26 | |
| 27 | CYAN = colorama.Fore.CYAN |
| 28 | GREEN = colorama.Fore.GREEN |
| 29 | MAGENTA = colorama.Fore.MAGENTA |
| 30 | RED = colorama.Fore.RED |
[email protected] | c050a5b | 2014-03-26 06:18:50 | [diff] [blame] | 31 | WHITE = colorama.Fore.WHITE |
[email protected] | 8bc9b5c | 2014-03-12 01:36:18 | [diff] [blame] | 32 | |
| 33 | BLUEBAK = colorama.Back.BLUE |
| 34 | |
| 35 | BRIGHT = colorama.Style.BRIGHT |
| 36 | RESET = colorama.Fore.RESET + colorama.Back.RESET + colorama.Style.RESET_ALL |
| 37 | |
[email protected] | c050a5b | 2014-03-26 06:18:50 | [diff] [blame] | 38 | # Git emits combined color |
| 39 | BRIGHT_RED = '\x1b[1;31m' |
| 40 | |
anatoly techtonik | c878ba6 | 2017-04-03 03:08:38 | [diff] [blame] | 41 | |
| 42 | def print_help(): |
| 43 | names = { |
| 44 | 'Cyan': CYAN, |
| 45 | 'Green': GREEN, |
| 46 | 'Magenta': MAGENTA, |
| 47 | 'Red': RED, |
| 48 | 'White': WHITE, |
| 49 | 'Blue background': BLUEBAK, |
| 50 | } |
| 51 | msg = "usage: git map [-h] [<args>]\n" |
| 52 | |
| 53 | for line in __doc__.splitlines(): |
| 54 | for key in names.keys(): |
| 55 | if key in line: |
| 56 | msg += line.replace('* ', '* ' + names[key])+RESET+'\n' |
| 57 | break |
| 58 | else: |
| 59 | msg += line + '\n' |
| 60 | sys.stdout.write(msg) |
| 61 | |
| 62 | |
[email protected] | 013731e | 2015-02-26 18:28:43 | [diff] [blame] | 63 | def main(argv): |
anatoly techtonik | c878ba6 | 2017-04-03 03:08:38 | [diff] [blame] | 64 | if '-h' in argv: |
| 65 | print_help() |
| 66 | return 0 |
| 67 | |
agable | 7aa2ddd | 2016-06-21 14:47:00 | [diff] [blame] | 68 | map_extra = get_config_list('depot_tools.map_extra') |
Aaron Gable | 52e8abc | 2017-06-07 16:37:24 | [diff] [blame] | 69 | fmt = '%C(red bold)%h%x09%Creset%C(green)%d%Creset %C(yellow)%cd%Creset ~ %s' |
[email protected] | 8bc9b5c | 2014-03-12 01:36:18 | [diff] [blame] | 70 | log_proc = subprocess2.Popen( |
[email protected] | a8378e2 | 2014-03-26 18:53:32 | [diff] [blame] | 71 | [GIT_EXE, 'log', '--graph', '--branches', '--tags', root(), |
| 72 | '--color=always', '--date=short', ('--pretty=format:' + fmt) |
[email protected] | 013731e | 2015-02-26 18:28:43 | [diff] [blame] | 73 | ] + map_extra + argv, |
[email protected] | 8bc9b5c | 2014-03-12 01:36:18 | [diff] [blame] | 74 | stdout=subprocess2.PIPE, |
| 75 | shell=False) |
| 76 | |
| 77 | current = current_branch() |
| 78 | all_branches = set(branches()) |
[email protected] | 6332b15 | 2014-05-15 03:16:10 | [diff] [blame] | 79 | merge_base_map = {b: get_or_create_merge_base(b) for b in all_branches} |
[email protected] | 10fbe87 | 2014-05-16 22:31:13 | [diff] [blame] | 80 | merge_base_map = {b: v for b, v in merge_base_map.iteritems() if v} |
[email protected] | 8bc9b5c | 2014-03-12 01:36:18 | [diff] [blame] | 81 | if current in all_branches: |
| 82 | all_branches.remove(current) |
| 83 | all_tags = set(tags()) |
| 84 | try: |
| 85 | for line in log_proc.stdout.xreadlines(): |
[email protected] | c050a5b | 2014-03-26 06:18:50 | [diff] [blame] | 86 | if merge_base_map: |
| 87 | commit = line[line.find(BRIGHT_RED)+len(BRIGHT_RED):line.find('\t')] |
| 88 | base_for_branches = set() |
| 89 | for branch, sha in merge_base_map.iteritems(): |
| 90 | if sha.startswith(commit): |
| 91 | base_for_branches.add(branch) |
| 92 | if base_for_branches: |
| 93 | newline = '\r\n' if line.endswith('\r\n') else '\n' |
| 94 | line = line.rstrip(newline) |
| 95 | line += ''.join( |
| 96 | (BRIGHT, WHITE, ' <(%s)' % (', '.join(base_for_branches)), |
[email protected] | cfa516f | 2014-04-04 17:13:33 | [diff] [blame] | 97 | RESET, newline)) |
[email protected] | c050a5b | 2014-03-26 06:18:50 | [diff] [blame] | 98 | for b in base_for_branches: |
| 99 | del merge_base_map[b] |
| 100 | |
[email protected] | 8bc9b5c | 2014-03-12 01:36:18 | [diff] [blame] | 101 | start = line.find(GREEN+' (') |
| 102 | end = line.find(')', start) |
| 103 | if start != -1 and end != -1: |
| 104 | start += len(GREEN) + 2 |
| 105 | branch_list = line[start:end].split(', ') |
| 106 | branches_str = '' |
| 107 | if branch_list: |
| 108 | colored_branches = [] |
| 109 | head_marker = '' |
| 110 | for b in branch_list: |
| 111 | if b == "HEAD": |
| 112 | head_marker = BLUEBAK+BRIGHT+'*' |
| 113 | continue |
| 114 | if b == current: |
| 115 | colored_branches.append(CYAN+BRIGHT+b+RESET) |
| 116 | current = None |
| 117 | elif b in all_branches: |
| 118 | colored_branches.append(GREEN+BRIGHT+b+RESET) |
| 119 | all_branches.remove(b) |
| 120 | elif b in all_tags: |
| 121 | colored_branches.append(MAGENTA+BRIGHT+b+RESET) |
| 122 | elif b.startswith('tag: '): |
| 123 | colored_branches.append(MAGENTA+BRIGHT+b[5:]+RESET) |
| 124 | else: |
| 125 | colored_branches.append(RED+b) |
| 126 | branches_str = '(%s) ' % ((GREEN+", ").join(colored_branches)+GREEN) |
| 127 | line = "%s%s%s" % (line[:start-1], branches_str, line[end+5:]) |
| 128 | if head_marker: |
| 129 | line = line.replace('*', head_marker, 1) |
| 130 | sys.stdout.write(line) |
| 131 | except (IOError, KeyboardInterrupt): |
| 132 | pass |
| 133 | finally: |
| 134 | sys.stderr.close() |
| 135 | sys.stdout.close() |
| 136 | return 0 |
| 137 | |
| 138 | |
| 139 | if __name__ == '__main__': |
[email protected] | 013731e | 2015-02-26 18:28:43 | [diff] [blame] | 140 | try: |
| 141 | sys.exit(main(sys.argv[1:])) |
| 142 | except KeyboardInterrupt: |
| 143 | sys.stderr.write('interrupted\n') |
| 144 | sys.exit(1) |