blob: e8d4513d942e790bbbc1bd5990738738f0c44af5 [file] [log] [blame]
Edward Lesmes98eda3f2019-08-12 21:09:53 +00001#!/usr/bin/env python
iannucci@chromium.orga112f032014-03-13 07:47:50 +00002# 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.org8bc9b5c2014-03-12 01:36:18 +00006"""
Edward Lemurb800fde2020-01-10 23:04:44 +00007usage: git map [-h] [--help] [<args>]
8
anatoly techtonikc878ba62017-04-03 06:08:38 +03009Enhances `git log --graph` view with information on commit branches + tags that
10point to them. Items are colorized as follows:
11
iannucci@chromium.org8bc9b5c2014-03-12 01:36:18 +000012 * Cyan - Currently checked out branch
13 * Green - Local branch
14 * Red - Remote branches
15 * Magenta - Tags
iannucci@chromium.orgc050a5b2014-03-26 06:18:50 +000016 * White - Merge Base Markers
iannucci@chromium.org8bc9b5c2014-03-12 01:36:18 +000017 * Blue background - The currently checked out commit
18"""
iannucci@chromium.orgc050a5b2014-03-26 06:18:50 +000019
Edward Lemurb800fde2020-01-10 23:04:44 +000020from __future__ import unicode_literals
21
22import os
iannucci@chromium.org8bc9b5c2014-03-12 01:36:18 +000023import sys
24
Edward Lemurb800fde2020-01-10 23:04:44 +000025import git_common
26import setup_color
iannucci@chromium.org8bc9b5c2014-03-12 01:36:18 +000027import subprocess2
28
iannucci@chromium.org8bc9b5c2014-03-12 01:36:18 +000029from third_party import colorama
30
iannucci@chromium.org8bc9b5c2014-03-12 01:36:18 +000031
Edward Lemurb800fde2020-01-10 23:04:44 +000032if sys.version_info.major == 2:
33 # On Python 3, BrokenPipeError is raised instead.
34 BrokenPipeError = IOError
iannucci@chromium.org8bc9b5c2014-03-12 01:36:18 +000035
Edward Lemurb800fde2020-01-10 23:04:44 +000036
iannucci@chromium.org8bc9b5c2014-03-12 01:36:18 +000037RESET = colorama.Fore.RESET + colorama.Back.RESET + colorama.Style.RESET_ALL
Edward Lemurb800fde2020-01-10 23:04:44 +000038BRIGHT = colorama.Style.BRIGHT
iannucci@chromium.org8bc9b5c2014-03-12 01:36:18 +000039
Edward Lemurb800fde2020-01-10 23:04:44 +000040BLUE_BACK = colorama.Back.BLUE + BRIGHT
41BRIGHT_RED = colorama.Fore.RED + BRIGHT
42CYAN = colorama.Fore.CYAN + BRIGHT
43GREEN = colorama.Fore.GREEN + BRIGHT
44MAGENTA = colorama.Fore.MAGENTA + BRIGHT
45RED = colorama.Fore.RED
46WHITE = colorama.Fore.WHITE + BRIGHT
47YELLOW = colorama.Fore.YELLOW
iannucci@chromium.orgc050a5b2014-03-26 06:18:50 +000048
anatoly techtonikc878ba62017-04-03 06:08:38 +030049
Edward Lemurb800fde2020-01-10 23:04:44 +000050def _print_help(outbuf):
anatoly techtonikc878ba62017-04-03 06:08:38 +030051 names = {
52 'Cyan': CYAN,
53 'Green': GREEN,
54 'Magenta': MAGENTA,
55 'Red': RED,
56 'White': WHITE,
Edward Lemurb800fde2020-01-10 23:04:44 +000057 'Blue background': BLUE_BACK,
anatoly techtonikc878ba62017-04-03 06:08:38 +030058 }
Edward Lemurb800fde2020-01-10 23:04:44 +000059 msg = ''
anatoly techtonikc878ba62017-04-03 06:08:38 +030060 for line in __doc__.splitlines():
Edward Lemurb800fde2020-01-10 23:04:44 +000061 for name, color in names.items():
62 if name in line:
63 msg += line.replace('* ' + name, color + '* ' + name + RESET) + '\n'
anatoly techtonikc878ba62017-04-03 06:08:38 +030064 break
65 else:
66 msg += line + '\n'
Edward Lemurb800fde2020-01-10 23:04:44 +000067 outbuf.write(msg.encode('utf-8', 'replace'))
anatoly techtonikc878ba62017-04-03 06:08:38 +030068
69
Edward Lemurb800fde2020-01-10 23:04:44 +000070def _color_branch(branch, all_branches, all_tags, current):
71 if branch == current or branch == 'HEAD -> ' + current:
72 color = CYAN
73 current = None
74 elif branch in all_branches:
75 color = GREEN
76 all_branches.remove(branch)
77 elif branch in all_tags:
78 color = MAGENTA
79 elif branch.startswith('tag: '):
80 color = MAGENTA
81 branch = branch[len('tag: '):]
82 else:
83 color = RED
84 return color + branch + RESET
85
86
87def _color_branch_list(branch_list, all_branches, all_tags, current):
88 if not branch_list:
89 return ''
90 colored_branches = (GREEN + ', ').join(
91 _color_branch(branch, all_branches, all_tags, current)
92 for branch in branch_list if branch != 'HEAD')
93 return (GREEN + '(' + colored_branches + GREEN + ') ' + RESET)
94
95
96def _parse_log_line(line):
97 graph, branch_list, commit_date, subject = (
98 line.decode('utf-8', 'replace').strip().split('\x00'))
99 branch_list = [] if not branch_list else branch_list.split(', ')
100 commit = graph.split()[-1]
101 graph = graph[:-len(commit)]
102 return graph, commit, branch_list, commit_date, subject
103
104
105def main(argv, outbuf):
106 if '-h' in argv or '--help' in argv:
107 _print_help(outbuf)
anatoly techtonikc878ba62017-04-03 06:08:38 +0300108 return 0
109
Edward Lemurb800fde2020-01-10 23:04:44 +0000110 map_extra = git_common.get_config_list('depot_tools.map_extra')
111 cmd = [
112 git_common.GIT_EXE, 'log', git_common.root(),
113 '--graph', '--branches', '--tags', '--color=always', '--date=short',
114 '--pretty=format:%H%x00%D%x00%cd%x00%s'
115 ] + map_extra + argv
iannucci@chromium.org8bc9b5c2014-03-12 01:36:18 +0000116
Edward Lemurb800fde2020-01-10 23:04:44 +0000117 log_proc = subprocess2.Popen(cmd, stdout=subprocess2.PIPE, shell=False)
118
119 current = git_common.current_branch()
120 all_tags = set(git_common.tags())
121 all_branches = set(git_common.branches())
iannucci@chromium.org8bc9b5c2014-03-12 01:36:18 +0000122 if current in all_branches:
123 all_branches.remove(current)
iannucci@chromium.orgc050a5b2014-03-26 06:18:50 +0000124
Edward Lemurb800fde2020-01-10 23:04:44 +0000125 merge_base_map = {}
126 for branch in all_branches:
127 merge_base = git_common.get_or_create_merge_base(branch)
128 if merge_base:
129 merge_base_map.setdefault(merge_base, set()).add(branch)
130
131 for merge_base, branches in merge_base_map.items():
132 merge_base_map[merge_base] = ', '.join(branches)
133
134 try:
135 for line in log_proc.stdout:
136 if b'\x00' not in line:
137 outbuf.write(line)
138 continue
139
140 graph, commit, branch_list, commit_date, subject = _parse_log_line(line)
141
142 if 'HEAD' in branch_list:
143 graph = graph.replace('*', BLUE_BACK + '*')
144
145 line = '{graph}{commit}\t{branches}{date} ~ {subject}'.format(
146 graph=graph,
147 commit=BRIGHT_RED + commit[:10] + RESET,
148 branches=_color_branch_list(
149 branch_list, all_branches, all_tags, current),
150 date=YELLOW + commit_date + RESET,
151 subject=subject)
152
153 if commit in merge_base_map:
154 line += ' <({})'.format(WHITE + merge_base_map[commit] + RESET)
155
156 line += os.linesep
157 outbuf.write(line.encode('utf-8', 'replace'))
158 except (BrokenPipeError, KeyboardInterrupt):
iannucci@chromium.org8bc9b5c2014-03-12 01:36:18 +0000159 pass
iannucci@chromium.org8bc9b5c2014-03-12 01:36:18 +0000160 return 0
161
162
163if __name__ == '__main__':
Edward Lemurb800fde2020-01-10 23:04:44 +0000164 setup_color.init()
165 with git_common.less() as less_input:
166 sys.exit(main(sys.argv[1:], less_input))