xref: /openbmc/u-boot/scripts/mailmapper (revision 31e2141d)
145765eedSMasahiro Yamada#!/usr/bin/env python
245765eedSMasahiro Yamada#
345765eedSMasahiro Yamada# Copyright (C) 2014, Masahiro Yamada <yamada.m@jp.panasonic.com>
445765eedSMasahiro Yamada#
545765eedSMasahiro Yamada# SPDX-License-Identifier:	GPL-2.0+
645765eedSMasahiro Yamada#
745765eedSMasahiro Yamada
845765eedSMasahiro Yamada'''
945765eedSMasahiro YamadaA tool to create/update the mailmap file
1045765eedSMasahiro Yamada
1145765eedSMasahiro YamadaThe command 'git shortlog' summarizes git log output in a format suitable
1245765eedSMasahiro Yamadafor inclusion in release announcements. Each commit will be grouped by
1345765eedSMasahiro Yamadaauthor and title.
1445765eedSMasahiro Yamada
1545765eedSMasahiro YamadaOne problem is that the authors' name and/or email address is sometimes
1645765eedSMasahiro Yamadaspelled differently. The .mailmap feature can be used to coalesce together
1745765eedSMasahiro Yamadacommits by the same persion.
1845765eedSMasahiro Yamada(See 'man git-shortlog' for furthur information of this feature.)
1945765eedSMasahiro Yamada
2045765eedSMasahiro YamadaThis tool helps to create/update the mailmap file.
2145765eedSMasahiro Yamada
2245765eedSMasahiro YamadaIt runs 'git shortlog' internally and searches differently spelled author
2345765eedSMasahiro Yamadanames which share the same email address. The author name with the most
2445765eedSMasahiro Yamadacommits is asuumed to be a canonical real name. If the number of commits
2545765eedSMasahiro Yamadafrom the cananonical name is equal to or greater than 'MIN_COMMITS',
2645765eedSMasahiro Yamadathe entry for the cananical name will be output. ('MIN_COMMITS' is used
2745765eedSMasahiro Yamadahere because we do not want to create a fat mailmap by adding every author
2845765eedSMasahiro Yamadawith only a few commits.)
2945765eedSMasahiro Yamada
3045765eedSMasahiro YamadaIf there exists a mailmap file specified by the mailmap.file configuration
3145765eedSMasahiro Yamadaoptions or '.mailmap' at the toplevel of the repository, it is used as
3245765eedSMasahiro Yamadaa base file. (The mailmap.file configuration takes precedence over the
3345765eedSMasahiro Yamada'.mailmap' file if both exist.)
3445765eedSMasahiro Yamada
3545765eedSMasahiro YamadaThe base file and the newly added entries are merged together and sorted
3645765eedSMasahiro Yamadaalphabetically (but the comment block is kept untouched), and then printed
3745765eedSMasahiro Yamadato standard output.
3845765eedSMasahiro Yamada
3945765eedSMasahiro YamadaUsage
4045765eedSMasahiro Yamada-----
4145765eedSMasahiro Yamada
4245765eedSMasahiro Yamada  scripts/mailmapper
4345765eedSMasahiro Yamada
4445765eedSMasahiro Yamadaprints the mailmapping to standard output.
4545765eedSMasahiro Yamada
4645765eedSMasahiro Yamada  scripts/mailmapper > tmp; mv tmp .mailmap
4745765eedSMasahiro Yamada
4845765eedSMasahiro Yamadawill be useful for updating '.mailmap' file.
4945765eedSMasahiro Yamada'''
5045765eedSMasahiro Yamada
5145765eedSMasahiro Yamadaimport sys
5245765eedSMasahiro Yamadaimport os
5345765eedSMasahiro Yamadaimport subprocess
5445765eedSMasahiro Yamada
5545765eedSMasahiro Yamada# The entries only for the canonical names with MIN_COMMITS or more commits.
5645765eedSMasahiro Yamada# This limitation is used so as not to create a too big mailmap file.
5745765eedSMasahiro YamadaMIN_COMMITS = 50
5845765eedSMasahiro Yamada
5945765eedSMasahiro Yamadatry:
6045765eedSMasahiro Yamada    toplevel = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'])
6145765eedSMasahiro Yamadaexcept subprocess.CalledProcessError:
62*31e2141dSMasahiro Yamada    sys.exit('Please run in a git repository.')
6345765eedSMasahiro Yamada
6445765eedSMasahiro Yamada# strip '\n'
6545765eedSMasahiro Yamadatoplevel = toplevel.rstrip()
6645765eedSMasahiro Yamada
6745765eedSMasahiro Yamada# Change the current working directory to the toplevel of the respository
6845765eedSMasahiro Yamada# for our easier life.
6945765eedSMasahiro Yamadaos.chdir(toplevel)
7045765eedSMasahiro Yamada
7145765eedSMasahiro Yamada# First, create 'auther name' vs 'number of commits' database.
7245765eedSMasahiro Yamada# We assume the name with the most commits as the canonical real name.
7345765eedSMasahiro Yamadashortlog = subprocess.check_output(['git', 'shortlog', '-s', '-n'])
7445765eedSMasahiro Yamada
7545765eedSMasahiro Yamadacommits_per_name = {}
7645765eedSMasahiro Yamada
7745765eedSMasahiro Yamadafor line in shortlog.splitlines():
7845765eedSMasahiro Yamada    try:
7945765eedSMasahiro Yamada        commits, name = line.split(None, 1)
8045765eedSMasahiro Yamada    except ValueError:
8145765eedSMasahiro Yamada        # ignore lines with an empty author name
8245765eedSMasahiro Yamada        pass
8345765eedSMasahiro Yamada    commits_per_name[name] = int(commits)
8445765eedSMasahiro Yamada
8545765eedSMasahiro Yamada# Next, coalesce the auther names with the same email address
8645765eedSMasahiro Yamadashortlog = subprocess.check_output(['git', 'shortlog', '-s', '-n', '-e'])
8745765eedSMasahiro Yamada
8845765eedSMasahiro Yamadamail_vs_name = {}
8945765eedSMasahiro Yamadaoutput = {}
9045765eedSMasahiro Yamada
9145765eedSMasahiro Yamadafor line in shortlog.splitlines():
9245765eedSMasahiro Yamada    # tmp, mail = line.rsplit(None, 1) is not safe
9345765eedSMasahiro Yamada    # because weird email addresses might include whitespaces
9445765eedSMasahiro Yamada    tmp, mail = line.split('<')
9545765eedSMasahiro Yamada    mail = '<' + mail.rstrip()
9645765eedSMasahiro Yamada    try:
9745765eedSMasahiro Yamada        _, name = tmp.rstrip().split(None, 1)
9845765eedSMasahiro Yamada    except ValueError:
9945765eedSMasahiro Yamada        # author name is empty
10045765eedSMasahiro Yamada        name = ''
10145765eedSMasahiro Yamada    if mail in mail_vs_name:
10245765eedSMasahiro Yamada        # another name for the same email address
10345765eedSMasahiro Yamada        prev_name = mail_vs_name[mail]
10445765eedSMasahiro Yamada        # Take the name with more commits
10545765eedSMasahiro Yamada        major_name = sorted([prev_name, name],
10645765eedSMasahiro Yamada                            key=lambda x: commits_per_name[x] if x else 0)[1]
10745765eedSMasahiro Yamada        mail_vs_name[mail] = major_name
10845765eedSMasahiro Yamada        if commits_per_name[major_name] > MIN_COMMITS:
10945765eedSMasahiro Yamada            output[mail] = major_name
11045765eedSMasahiro Yamada    else:
11145765eedSMasahiro Yamada        mail_vs_name[mail] = name
11245765eedSMasahiro Yamada
11345765eedSMasahiro Yamada# [1] If there exists a mailmap file at the location pointed to
11445765eedSMasahiro Yamada#     by the mailmap.file configuration option, update it.
11545765eedSMasahiro Yamada# [2] If the file .mailmap exists at the toplevel of the repository, update it.
11645765eedSMasahiro Yamada# [3] Otherwise, create a new mailmap file.
11745765eedSMasahiro Yamadamailmap_files = []
11845765eedSMasahiro Yamada
11945765eedSMasahiro Yamadatry:
12045765eedSMasahiro Yamada    config_mailmap = subprocess.check_output(['git', 'config', 'mailmap.file'])
12145765eedSMasahiro Yamadaexcept subprocess.CalledProcessError:
12245765eedSMasahiro Yamada    config_mailmap = ''
12345765eedSMasahiro Yamada
12445765eedSMasahiro Yamadaconfig_mailmap = config_mailmap.rstrip()
12545765eedSMasahiro Yamadaif config_mailmap:
12645765eedSMasahiro Yamada    mailmap_files.append(config_mailmap)
12745765eedSMasahiro Yamada
12845765eedSMasahiro Yamadamailmap_files.append('.mailmap')
12945765eedSMasahiro Yamada
13045765eedSMasahiro Yamadainfile = None
13145765eedSMasahiro Yamada
13245765eedSMasahiro Yamadafor map_file in mailmap_files:
13345765eedSMasahiro Yamada    try:
13445765eedSMasahiro Yamada        infile = open(map_file)
13545765eedSMasahiro Yamada    except:
13645765eedSMasahiro Yamada        # Failed to open. Try next.
13745765eedSMasahiro Yamada        continue
13845765eedSMasahiro Yamada    break
13945765eedSMasahiro Yamada
14045765eedSMasahiro Yamadacomment_block = []
14145765eedSMasahiro Yamadaoutput_lines = []
14245765eedSMasahiro Yamada
14345765eedSMasahiro Yamadaif infile:
14445765eedSMasahiro Yamada    for line in infile:
14545765eedSMasahiro Yamada        if line[0] == '#' or line[0] == '\n':
14645765eedSMasahiro Yamada            comment_block.append(line)
14745765eedSMasahiro Yamada        else:
14845765eedSMasahiro Yamada            output_lines.append(line)
14945765eedSMasahiro Yamada            break
15045765eedSMasahiro Yamada    for line in infile:
15145765eedSMasahiro Yamada        output_lines.append(line)
15245765eedSMasahiro Yamada    infile.close()
15345765eedSMasahiro Yamada
15445765eedSMasahiro Yamadafor mail, name in output.items():
15545765eedSMasahiro Yamada    output_lines.append(name + ' ' + mail + '\n')
15645765eedSMasahiro Yamada
15745765eedSMasahiro Yamadaoutput_lines.sort()
15845765eedSMasahiro Yamada
15945765eedSMasahiro Yamadasys.stdout.write(''.join(comment_block + output_lines))
160