xref: /openbmc/u-boot/scripts/mailmapper (revision 45765eeda2d0eae209c50d82444ba85cdbad33eb)
1*45765eedSMasahiro Yamada#!/usr/bin/env python
2*45765eedSMasahiro Yamada#
3*45765eedSMasahiro Yamada# Copyright (C) 2014, Masahiro Yamada <yamada.m@jp.panasonic.com>
4*45765eedSMasahiro Yamada#
5*45765eedSMasahiro Yamada# SPDX-License-Identifier:	GPL-2.0+
6*45765eedSMasahiro Yamada#
7*45765eedSMasahiro Yamada
8*45765eedSMasahiro Yamada'''
9*45765eedSMasahiro YamadaA tool to create/update the mailmap file
10*45765eedSMasahiro Yamada
11*45765eedSMasahiro YamadaThe command 'git shortlog' summarizes git log output in a format suitable
12*45765eedSMasahiro Yamadafor inclusion in release announcements. Each commit will be grouped by
13*45765eedSMasahiro Yamadaauthor and title.
14*45765eedSMasahiro Yamada
15*45765eedSMasahiro YamadaOne problem is that the authors' name and/or email address is sometimes
16*45765eedSMasahiro Yamadaspelled differently. The .mailmap feature can be used to coalesce together
17*45765eedSMasahiro Yamadacommits by the same persion.
18*45765eedSMasahiro Yamada(See 'man git-shortlog' for furthur information of this feature.)
19*45765eedSMasahiro Yamada
20*45765eedSMasahiro YamadaThis tool helps to create/update the mailmap file.
21*45765eedSMasahiro Yamada
22*45765eedSMasahiro YamadaIt runs 'git shortlog' internally and searches differently spelled author
23*45765eedSMasahiro Yamadanames which share the same email address. The author name with the most
24*45765eedSMasahiro Yamadacommits is asuumed to be a canonical real name. If the number of commits
25*45765eedSMasahiro Yamadafrom the cananonical name is equal to or greater than 'MIN_COMMITS',
26*45765eedSMasahiro Yamadathe entry for the cananical name will be output. ('MIN_COMMITS' is used
27*45765eedSMasahiro Yamadahere because we do not want to create a fat mailmap by adding every author
28*45765eedSMasahiro Yamadawith only a few commits.)
29*45765eedSMasahiro Yamada
30*45765eedSMasahiro YamadaIf there exists a mailmap file specified by the mailmap.file configuration
31*45765eedSMasahiro Yamadaoptions or '.mailmap' at the toplevel of the repository, it is used as
32*45765eedSMasahiro Yamadaa base file. (The mailmap.file configuration takes precedence over the
33*45765eedSMasahiro Yamada'.mailmap' file if both exist.)
34*45765eedSMasahiro Yamada
35*45765eedSMasahiro YamadaThe base file and the newly added entries are merged together and sorted
36*45765eedSMasahiro Yamadaalphabetically (but the comment block is kept untouched), and then printed
37*45765eedSMasahiro Yamadato standard output.
38*45765eedSMasahiro Yamada
39*45765eedSMasahiro YamadaUsage
40*45765eedSMasahiro Yamada-----
41*45765eedSMasahiro Yamada
42*45765eedSMasahiro Yamada  scripts/mailmapper
43*45765eedSMasahiro Yamada
44*45765eedSMasahiro Yamadaprints the mailmapping to standard output.
45*45765eedSMasahiro Yamada
46*45765eedSMasahiro Yamada  scripts/mailmapper > tmp; mv tmp .mailmap
47*45765eedSMasahiro Yamada
48*45765eedSMasahiro Yamadawill be useful for updating '.mailmap' file.
49*45765eedSMasahiro Yamada'''
50*45765eedSMasahiro Yamada
51*45765eedSMasahiro Yamadaimport sys
52*45765eedSMasahiro Yamadaimport os
53*45765eedSMasahiro Yamadaimport subprocess
54*45765eedSMasahiro Yamada
55*45765eedSMasahiro Yamada# The entries only for the canonical names with MIN_COMMITS or more commits.
56*45765eedSMasahiro Yamada# This limitation is used so as not to create a too big mailmap file.
57*45765eedSMasahiro YamadaMIN_COMMITS = 50
58*45765eedSMasahiro Yamada
59*45765eedSMasahiro Yamadatry:
60*45765eedSMasahiro Yamada    toplevel = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'])
61*45765eedSMasahiro Yamadaexcept subprocess.CalledProcessError:
62*45765eedSMasahiro Yamada    print >> sys.stderr, 'Please run in a git repository.'
63*45765eedSMasahiro Yamada    sys.exit(1)
64*45765eedSMasahiro Yamada
65*45765eedSMasahiro Yamada# strip '\n'
66*45765eedSMasahiro Yamadatoplevel = toplevel.rstrip()
67*45765eedSMasahiro Yamada
68*45765eedSMasahiro Yamada# Change the current working directory to the toplevel of the respository
69*45765eedSMasahiro Yamada# for our easier life.
70*45765eedSMasahiro Yamadaos.chdir(toplevel)
71*45765eedSMasahiro Yamada
72*45765eedSMasahiro Yamada# First, create 'auther name' vs 'number of commits' database.
73*45765eedSMasahiro Yamada# We assume the name with the most commits as the canonical real name.
74*45765eedSMasahiro Yamadashortlog = subprocess.check_output(['git', 'shortlog', '-s', '-n'])
75*45765eedSMasahiro Yamada
76*45765eedSMasahiro Yamadacommits_per_name = {}
77*45765eedSMasahiro Yamada
78*45765eedSMasahiro Yamadafor line in shortlog.splitlines():
79*45765eedSMasahiro Yamada    try:
80*45765eedSMasahiro Yamada        commits, name = line.split(None, 1)
81*45765eedSMasahiro Yamada    except ValueError:
82*45765eedSMasahiro Yamada        # ignore lines with an empty author name
83*45765eedSMasahiro Yamada        pass
84*45765eedSMasahiro Yamada    commits_per_name[name] = int(commits)
85*45765eedSMasahiro Yamada
86*45765eedSMasahiro Yamada# Next, coalesce the auther names with the same email address
87*45765eedSMasahiro Yamadashortlog = subprocess.check_output(['git', 'shortlog', '-s', '-n', '-e'])
88*45765eedSMasahiro Yamada
89*45765eedSMasahiro Yamadamail_vs_name = {}
90*45765eedSMasahiro Yamadaoutput = {}
91*45765eedSMasahiro Yamada
92*45765eedSMasahiro Yamadafor line in shortlog.splitlines():
93*45765eedSMasahiro Yamada    # tmp, mail = line.rsplit(None, 1) is not safe
94*45765eedSMasahiro Yamada    # because weird email addresses might include whitespaces
95*45765eedSMasahiro Yamada    tmp, mail = line.split('<')
96*45765eedSMasahiro Yamada    mail = '<' + mail.rstrip()
97*45765eedSMasahiro Yamada    try:
98*45765eedSMasahiro Yamada        _, name = tmp.rstrip().split(None, 1)
99*45765eedSMasahiro Yamada    except ValueError:
100*45765eedSMasahiro Yamada        # author name is empty
101*45765eedSMasahiro Yamada        name = ''
102*45765eedSMasahiro Yamada    if mail in mail_vs_name:
103*45765eedSMasahiro Yamada        # another name for the same email address
104*45765eedSMasahiro Yamada        prev_name = mail_vs_name[mail]
105*45765eedSMasahiro Yamada        # Take the name with more commits
106*45765eedSMasahiro Yamada        major_name = sorted([prev_name, name],
107*45765eedSMasahiro Yamada                            key=lambda x: commits_per_name[x] if x else 0)[1]
108*45765eedSMasahiro Yamada        mail_vs_name[mail] = major_name
109*45765eedSMasahiro Yamada        if commits_per_name[major_name] > MIN_COMMITS:
110*45765eedSMasahiro Yamada            output[mail] = major_name
111*45765eedSMasahiro Yamada    else:
112*45765eedSMasahiro Yamada        mail_vs_name[mail] = name
113*45765eedSMasahiro Yamada
114*45765eedSMasahiro Yamada# [1] If there exists a mailmap file at the location pointed to
115*45765eedSMasahiro Yamada#     by the mailmap.file configuration option, update it.
116*45765eedSMasahiro Yamada# [2] If the file .mailmap exists at the toplevel of the repository, update it.
117*45765eedSMasahiro Yamada# [3] Otherwise, create a new mailmap file.
118*45765eedSMasahiro Yamadamailmap_files = []
119*45765eedSMasahiro Yamada
120*45765eedSMasahiro Yamadatry:
121*45765eedSMasahiro Yamada    config_mailmap = subprocess.check_output(['git', 'config', 'mailmap.file'])
122*45765eedSMasahiro Yamadaexcept subprocess.CalledProcessError:
123*45765eedSMasahiro Yamada    config_mailmap = ''
124*45765eedSMasahiro Yamada
125*45765eedSMasahiro Yamadaconfig_mailmap = config_mailmap.rstrip()
126*45765eedSMasahiro Yamadaif config_mailmap:
127*45765eedSMasahiro Yamada    mailmap_files.append(config_mailmap)
128*45765eedSMasahiro Yamada
129*45765eedSMasahiro Yamadamailmap_files.append('.mailmap')
130*45765eedSMasahiro Yamada
131*45765eedSMasahiro Yamadainfile = None
132*45765eedSMasahiro Yamada
133*45765eedSMasahiro Yamadafor map_file in mailmap_files:
134*45765eedSMasahiro Yamada    try:
135*45765eedSMasahiro Yamada        infile = open(map_file)
136*45765eedSMasahiro Yamada    except:
137*45765eedSMasahiro Yamada        # Failed to open. Try next.
138*45765eedSMasahiro Yamada        continue
139*45765eedSMasahiro Yamada    break
140*45765eedSMasahiro Yamada
141*45765eedSMasahiro Yamadacomment_block = []
142*45765eedSMasahiro Yamadaoutput_lines = []
143*45765eedSMasahiro Yamada
144*45765eedSMasahiro Yamadaif infile:
145*45765eedSMasahiro Yamada    for line in infile:
146*45765eedSMasahiro Yamada        if line[0] == '#' or line[0] == '\n':
147*45765eedSMasahiro Yamada            comment_block.append(line)
148*45765eedSMasahiro Yamada        else:
149*45765eedSMasahiro Yamada            output_lines.append(line)
150*45765eedSMasahiro Yamada            break
151*45765eedSMasahiro Yamada    for line in infile:
152*45765eedSMasahiro Yamada        output_lines.append(line)
153*45765eedSMasahiro Yamada    infile.close()
154*45765eedSMasahiro Yamada
155*45765eedSMasahiro Yamadafor mail, name in output.items():
156*45765eedSMasahiro Yamada    output_lines.append(name + ' ' + mail + '\n')
157*45765eedSMasahiro Yamada
158*45765eedSMasahiro Yamadaoutput_lines.sort()
159*45765eedSMasahiro Yamada
160*45765eedSMasahiro Yamadasys.stdout.write(''.join(comment_block + output_lines))
161