summaryrefslogtreecommitdiff
path: root/tools/docs/gen-renames.py
blob: 8cb3b2157d83b9d541d47d5e23344881783808f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#! /usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
#
# Copyright © 2025, Oracle and/or its affiliates.
# Author: Vegard Nossum <vegard.nossum@oracle.com>

"""Trawl repository history for renames of Documentation/**.rst files.

Example:

    tools/docs/gen-renames.py --rev HEAD > Documentation/.renames.txt
"""

import argparse
import itertools
import os
import subprocess
import sys

parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('--rev', default='HEAD', help='generate renames up to this revision')

args = parser.parse_args()

def normalize(path):
    prefix = 'Documentation/'
    suffix = '.rst'

    assert path.startswith(prefix)
    assert path.endswith(suffix)

    return path[len(prefix):-len(suffix)]

class Name(object):
    def __init__(self, name):
        self.names = [name]

    def rename(self, new_name):
        self.names.append(new_name)

names = {
}

for line in subprocess.check_output([
    'git', 'log',
    '--reverse',
    '--oneline',
    '--find-renames',
    '--diff-filter=RD',
    '--name-status',
    '--format=commit %H',
    # ~v4.8-ish is when Sphinx/.rst was added in the first place
    f'v4.8..{args.rev}',
    '--',
    'Documentation/'
], text=True).splitlines():
    # rename
    if line.startswith('R'):
        _, old, new = line[1:].split('\t', 2)

        if old.endswith('.rst') and new.endswith('.rst'):
            old = normalize(old)
            new = normalize(new)

            name = names.get(old)
            if name is None:
                name = Name(old)
            else:
                del names[old]

            name.rename(new)
            names[new] = name

        continue

    # delete
    if line.startswith('D'):
        _, old = line.split('\t', 1)

        if old.endswith('.rst'):
            old = normalize(old)

            # TODO: we could save added/modified files as well and propose
            # them as alternatives
            name = names.get(old)
            if name is None:
                pass
            else:
                del names[old]

        continue

#
# Get the set of current files so we can sanity check that we aren't
# redirecting any of those
#

current_files = set()
for line in subprocess.check_output([
    'git', 'ls-tree',
    '-r',
    '--name-only',
    args.rev,
    'Documentation/',
], text=True).splitlines():
    if line.endswith('.rst'):
        current_files.add(normalize(line))

#
# Format/group/output result
#

result = []
for _, v in names.items():
    old_names = v.names[:-1]
    new_name = v.names[-1]

    for old_name in old_names:
        if old_name == new_name:
            # A file was renamed to its new name twice; don't redirect that
            continue

        if old_name in current_files:
            # A file was recreated with a former name; don't redirect those
            continue

        result.append((old_name, new_name))

for old_name, new_name in sorted(result):
    print(f"{old_name} {new_name}")