--- /dev/null
+#!/usr/bin/env python3
+
+"""Helper for generating GNU ChangeLog entries."""
+
+import argparse
+import datetime
+import importlib.machinery
+import os
+from pathlib import Path
+import subprocess
+import sys
+
+import unidiff
+
+
+_loader = lambda *args: importlib.machinery.SourceFileLoader(*args).load_module()
+mklog = _loader('mklog', '/usr/local/src/gnu/gcc/git/contrib/mklog.py')
+del _loader
+
+
+NAME = 'Mike Frysinger <vapier@gentoo.org>'
+DATE = datetime.datetime.now().strftime('%Y-%m-%d')
+
+
+def get_parser():
+ """Get CLI parser."""
+ parser = argparse.ArgumentParser(description=__doc__)
+ return parser
+
+
+def main(argv):
+ """The main entry point for scripts."""
+ parser = get_parser()
+ opts = parser.parse_args(argv)
+
+ # Get the patchset from git.
+ full_diff = subprocess.run(
+ ['git', 'log', '-p', '--relative', '-1'],
+ check=True, capture_output=True, encoding='utf-8').stdout
+
+ # Filter out entries we don't want.
+ patchset = unidiff.PatchSet(full_diff)
+ i = 0
+ while i < len(patchset):
+ if Path(patchset[i].path).name.startswith('ChangeLog'):
+ patchset.pop(i)
+ else:
+ i += 1
+
+ # List of files that are always generated.
+ generated_files = {'aclocal.m4', 'config.in', 'configure'}
+
+ # Move the generated patches to the end of the patchset so the generated
+ # ChangeLog lists them at the end.
+ generated_patches = []
+ i = 0
+ while i < len(patchset):
+ if Path(patchset[i].path).name in generated_files:
+ generated_patches.append(patchset.pop(i))
+ else:
+ i += 1
+ patchset.extend(generated_patches)
+
+ # Find the ChangeLog for each path.
+ all_dirs = {Path(x.path).parent for x in patchset}
+ dirs_to_logs = {}
+ for index_dir in all_dirs:
+ d = index_dir
+ while True:
+ log = d / 'ChangeLog'
+ if log.is_file():
+ break
+ d = d.parent
+ dirs_to_logs[index_dir] = log
+
+ # Group the patches based on the ChangeLogs they'll go into.
+ logs_to_patches = {}
+ for pfile in patchset:
+ log = dirs_to_logs[Path(pfile.path).parent]
+ ps = logs_to_patches.setdefault(log, unidiff.PatchSet(''))
+ ps.append(pfile)
+
+ # Now generate the logs for each subdir.
+ for log, ps in logs_to_patches.items():
+ new_log = mklog.generate_changelog(str(ps))
+ new_log = '\n'.join(new_log.splitlines()[2:]).rstrip()
+
+ # Hack: If we rebased the changes to a subdir, strip the path down.
+ # e.g. bfin/foo.c goes into bfin/ChangeLog, so drop bfin/ prefix.
+ for pfile in ps:
+ relpath = os.path.relpath(pfile.path, log.parent)
+ if relpath != pfile.path:
+ new_log = new_log.replace(pfile.path, relpath)
+
+ # Read the old ChangeLog file and strip spurious whitespace.
+ with open(log, encoding='utf-8') as fp:
+ old_log = '\n'.join(x.rstrip() for x in fp.read().strip().splitlines())
+
+ # Now update the ChangeLog file with the new entry at top.
+ with open(log, 'w', encoding='utf-8') as fp:
+ fp.write(f'{DATE} {NAME}\n\n')
+ fp.write(new_log)
+ fp.write('\n\n')
+ fp.write(old_log)
+ fp.write('\n')
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))