Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1#! /usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3#
4# Copyright © 2025, Oracle and/or its affiliates.
5# Author: Vegard Nossum <vegard.nossum@oracle.com>
6
7"""Trawl repository history for renames of Documentation/**.rst files.
8
9Example:
10
11 tools/docs/gen-renames.py --rev HEAD > Documentation/.renames.txt
12"""
13
14import argparse
15import itertools
16import os
17import subprocess
18import sys
19
20parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
21parser.add_argument('--rev', default='HEAD', help='generate renames up to this revision')
22
23args = parser.parse_args()
24
25def normalize(path):
26 prefix = 'Documentation/'
27 suffix = '.rst'
28
29 assert path.startswith(prefix)
30 assert path.endswith(suffix)
31
32 return path[len(prefix):-len(suffix)]
33
34class Name(object):
35 def __init__(self, name):
36 self.names = [name]
37
38 def rename(self, new_name):
39 self.names.append(new_name)
40
41names = {
42}
43
44for line in subprocess.check_output([
45 'git', 'log',
46 '--reverse',
47 '--oneline',
48 '--find-renames',
49 '--diff-filter=RD',
50 '--name-status',
51 '--format=commit %H',
52 # ~v4.8-ish is when Sphinx/.rst was added in the first place
53 f'v4.8..{args.rev}',
54 '--',
55 'Documentation/'
56], text=True).splitlines():
57 # rename
58 if line.startswith('R'):
59 _, old, new = line[1:].split('\t', 2)
60
61 if old.endswith('.rst') and new.endswith('.rst'):
62 old = normalize(old)
63 new = normalize(new)
64
65 name = names.get(old)
66 if name is None:
67 name = Name(old)
68 else:
69 del names[old]
70
71 name.rename(new)
72 names[new] = name
73
74 continue
75
76 # delete
77 if line.startswith('D'):
78 _, old = line.split('\t', 1)
79
80 if old.endswith('.rst'):
81 old = normalize(old)
82
83 # TODO: we could save added/modified files as well and propose
84 # them as alternatives
85 name = names.get(old)
86 if name is None:
87 pass
88 else:
89 del names[old]
90
91 continue
92
93#
94# Get the set of current files so we can sanity check that we aren't
95# redirecting any of those
96#
97
98current_files = set()
99for line in subprocess.check_output([
100 'git', 'ls-tree',
101 '-r',
102 '--name-only',
103 args.rev,
104 'Documentation/',
105], text=True).splitlines():
106 if line.endswith('.rst'):
107 current_files.add(normalize(line))
108
109#
110# Format/group/output result
111#
112
113result = []
114for _, v in names.items():
115 old_names = v.names[:-1]
116 new_name = v.names[-1]
117
118 for old_name in old_names:
119 if old_name == new_name:
120 # A file was renamed to its new name twice; don't redirect that
121 continue
122
123 if old_name in current_files:
124 # A file was recreated with a former name; don't redirect those
125 continue
126
127 result.append((old_name, new_name))
128
129for old_name, new_name in sorted(result):
130 print(f"{old_name} {new_name}")