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