]>
Commit | Line | Data |
---|---|---|
478d9372 MY |
1 | #!/usr/bin/env python2 |
2 | # | |
3 | # Author: Masahiro Yamada <[email protected]> | |
4 | # | |
5 | # SPDX-License-Identifier: GPL-2.0+ | |
6 | # | |
7 | ||
8 | """ | |
9 | Fill the "Commit" and "Removed" fields of doc/README.scrapyard | |
10 | ||
11 | The file doc/README.scrapyard is used to keep track of removed boards. | |
12 | ||
13 | When we remove support for boards, we are supposed to add entries to | |
14 | doc/README.scrapyard leaving "Commit" and "Removed" fields blank. | |
15 | ||
16 | The "Commit" field is the commit hash in which the board was removed | |
17 | and the "Removed" is the date at which the board was removed. Those | |
18 | two are known only after the board removal patch was applied, thus they | |
19 | need to be filled in later. | |
20 | ||
21 | This effectively means that the person who removes other boards is | |
22 | supposed to fill in the blank fields before adding new entries to | |
23 | doc/README.scrapyard. | |
24 | ||
25 | That is a really tedious task that should be automated. | |
26 | This script fills the blank fields of doc/README.scrapyard for you! | |
27 | ||
28 | Usage: | |
29 | ||
30 | The "Commit" and "Removed" fields must be "-". The other fields should | |
31 | have already been filled in by a former commit. | |
32 | ||
33 | Run | |
34 | scripts/fill_scrapyard.py | |
35 | """ | |
36 | ||
37 | import os | |
38 | import subprocess | |
39 | import sys | |
40 | import tempfile | |
41 | ||
42 | DOC='doc/README.scrapyard' | |
43 | ||
44 | def get_last_modify_commit(file, line_num): | |
45 | """Get the commit that last modified the given line. | |
46 | ||
47 | This function runs "git blame" against the given line of the given | |
48 | file and returns the commit hash that last modified it. | |
49 | ||
50 | Arguments: | |
51 | file: the file to be git-blame'd. | |
52 | line_num: the line number to be git-blame'd. This line number | |
53 | starts from 1, not 0. | |
54 | ||
55 | Returns: | |
56 | Commit hash that last modified the line. The number of digits is | |
57 | long enough to form a unique commit. | |
58 | """ | |
59 | result = subprocess.check_output(['git', 'blame', '-L', | |
60 | '%d,%d' % (line_num, line_num), file]) | |
61 | commit = result.split()[0] | |
62 | ||
63 | if commit[0] == '^': | |
64 | sys.exit('%s: line %d: ' % (file, line_num) + | |
65 | 'this line was modified before the beginning of git history') | |
66 | ||
67 | if commit == '0' * len(commit): | |
68 | sys.exit('%s: line %d: locally modified\n' % (file, line_num) + | |
69 | 'Please run this script in a clean repository.') | |
70 | ||
71 | return commit | |
72 | ||
73 | def get_committer_date(commit): | |
74 | """Get the committer date of the given commit. | |
75 | ||
76 | This function returns the date when the given commit was applied. | |
77 | ||
78 | Arguments: | |
79 | commit: commit-ish object. | |
80 | ||
81 | Returns: | |
82 | The committer date of the given commit in the form YY-MM-DD. | |
83 | """ | |
84 | committer_date = subprocess.check_output(['git', 'show', '-s', | |
85 | '--format=%ci', commit]) | |
86 | return committer_date.split()[0] | |
87 | ||
88 | def move_to_topdir(): | |
89 | """Change directory to the top of the git repository. | |
90 | ||
91 | Or, exit with an error message if called out of a git repository. | |
92 | """ | |
93 | try: | |
94 | toplevel = subprocess.check_output(['git', 'rev-parse', | |
95 | '--show-toplevel']) | |
96 | except subprocess.CalledProcessError: | |
97 | sys.exit('Please run in a git repository.') | |
98 | ||
99 | # strip '\n' | |
100 | toplevel = toplevel.rstrip() | |
101 | ||
102 | # Change the current working directory to the toplevel of the respository | |
103 | # for our easier life. | |
104 | os.chdir(toplevel) | |
105 | ||
106 | class TmpFile: | |
107 | ||
108 | """Useful class to handle a temporary file. | |
109 | ||
110 | tempfile.mkstemp() is often used to create a unique temporary file, | |
111 | but what is inconvenient is that the caller is responsible for | |
112 | deleting the file when done with it. | |
113 | ||
114 | Even when the caller errors out on the way, the temporary file must | |
115 | be deleted somehow. The idea here is that we delete the file in | |
116 | the destructor of this class because the destructor is always | |
117 | invoked when the instance of the class is freed. | |
118 | """ | |
119 | ||
120 | def __init__(self): | |
121 | """Constructor - create a temporary file""" | |
122 | fd, self.filename = tempfile.mkstemp() | |
123 | self.file = os.fdopen(fd, 'w') | |
124 | ||
125 | def __del__(self): | |
126 | """Destructor - delete the temporary file""" | |
127 | try: | |
128 | os.remove(self.filename) | |
129 | except: | |
130 | pass | |
131 | ||
132 | def main(): | |
133 | move_to_topdir() | |
134 | ||
135 | line_num = 1 | |
136 | ||
137 | tmpfile = TmpFile() | |
138 | for line in open(DOC): | |
139 | tmp = line.split(None, 5) | |
140 | modified = False | |
141 | ||
142 | if len(tmp) >= 5: | |
143 | # fill "Commit" field | |
144 | if tmp[3] == '-': | |
145 | tmp[3] = get_last_modify_commit(DOC, line_num) | |
146 | modified = True | |
147 | # fill "Removed" field | |
148 | if tmp[4] == '-': | |
149 | tmp[4] = get_committer_date(tmp[3]) | |
150 | if modified: | |
151 | line = tmp[0].ljust(17) | |
152 | line += tmp[1].ljust(12) | |
153 | line += tmp[2].ljust(15) | |
154 | line += tmp[3].ljust(12) | |
155 | line += tmp[4].ljust(12) | |
156 | if len(tmp) >= 6: | |
157 | line += tmp[5] | |
158 | line = line.rstrip() + '\n' | |
159 | ||
160 | tmpfile.file.write(line) | |
161 | line_num += 1 | |
162 | ||
163 | os.rename(tmpfile.filename, DOC) | |
164 | ||
165 | if __name__ == '__main__': | |
166 | main() |