]> git.ipfire.org Git - thirdparty/gcc.git/blob - contrib/gcc-changelog/git_email.py
Update copyright years.
[thirdparty/gcc.git] / contrib / gcc-changelog / git_email.py
1 #!/usr/bin/env python3
2
3 # Copyright (C) 2020-2024 Free Software Foundation, Inc.
4 #
5 # This file is part of GCC.
6 #
7 # GCC is free software; you can redistribute it and/or modify it under
8 # the terms of the GNU General Public License as published by the Free
9 # Software Foundation; either version 3, or (at your option) any later
10 # version.
11 #
12 # GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 # for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with GCC; see the file COPYING3. If not see
19 # <http://www.gnu.org/licenses/>. */
20
21 import os
22 import re
23 import sys
24 from itertools import takewhile
25
26 from dateutil.parser import parse
27
28 from git_commit import GitCommit, GitInfo, decode_path
29
30 from unidiff import PatchSet, PatchedFile
31
32 DATE_PREFIX = 'Date: '
33 FROM_PREFIX = 'From: '
34 SUBJECT_PREFIX = 'Subject: '
35 subject_patch_regex = re.compile(r'^\[PATCH( \d+/\d+)?\] ')
36 unidiff_supports_renaming = hasattr(PatchedFile(), 'is_rename')
37
38
39 class GitEmail(GitCommit):
40 def __init__(self, filename):
41 self.filename = filename
42 date = None
43 author = None
44 subject = ''
45
46 subject_last = False
47 with open(self.filename, newline='\n') as f:
48 data = f.read()
49 diff = PatchSet(data)
50 lines = data.splitlines()
51 lines = list(takewhile(lambda line: line != '---', lines))
52 for line in lines:
53 if line.startswith(DATE_PREFIX):
54 date = parse(line[len(DATE_PREFIX):])
55 elif line.startswith(FROM_PREFIX):
56 author = GitCommit.format_git_author(line[len(FROM_PREFIX):])
57 elif line.startswith(SUBJECT_PREFIX):
58 subject = line[len(SUBJECT_PREFIX):]
59 subject_last = True
60 elif subject_last and line.startswith(' '):
61 subject += line
62 elif line == '':
63 break
64 else:
65 subject_last = False
66
67 if subject:
68 subject = subject_patch_regex.sub('', subject)
69 header = list(takewhile(lambda line: line != '', lines))
70 # Note: commit message consists of email subject, empty line, email body
71 message = [subject] + lines[len(header):]
72
73 modified_files = []
74 for f in diff:
75 # Strip "a/" and "b/" prefixes
76 source = decode_path(f.source_file)[2:]
77 target = decode_path(f.target_file)[2:]
78
79 if f.is_added_file:
80 t = 'A'
81 elif f.is_removed_file:
82 t = 'D'
83 elif unidiff_supports_renaming and f.is_rename:
84 # Consider that renamed files are two operations: the deletion
85 # of the original name and the addition of the new one.
86 modified_files.append((source, 'D'))
87 t = 'A'
88 else:
89 t = 'M'
90 modified_files.append((target if t != 'D' else source, t))
91 git_info = GitInfo(None, date, author, message, modified_files)
92 super().__init__(git_info, commit_to_info_hook=None)
93
94
95 def show_help():
96 print("""usage: git_email.py [--help] [patch file ...]
97
98 Check git ChangeLog format of a patch
99
100 With zero arguments, process every patch file in the
101 ./patches directory.
102 With one argument, process the named patch file.
103
104 Patch files must be in 'git format-patch' format.""")
105 sys.exit(0)
106
107
108 if __name__ == '__main__':
109 if len(sys.argv) == 2 and (sys.argv[1] == '-h' or sys.argv[1] == '--help'):
110 show_help()
111
112 if len(sys.argv) == 1:
113 allfiles = []
114 for root, _dirs, files in os.walk('patches'):
115 for f in files:
116 full = os.path.join(root, f)
117 allfiles.append(full)
118
119 success = 0
120 for full in sorted(allfiles):
121 email = GitEmail(full)
122 print(email.filename)
123 if email.success:
124 success += 1
125 print(' OK')
126 for warning in email.warnings:
127 print(' WARN: %s' % warning)
128 else:
129 for error in email.errors:
130 print(' ERR: %s' % error)
131
132 print()
133 print('Successfully parsed: %d/%d' % (success, len(allfiles)))
134 else:
135 email = GitEmail(sys.argv[1])
136 if email.success:
137 print('OK')
138 email.print_output()
139 email.print_warnings()
140 else:
141 if not email.info.lines:
142 print('Error: patch contains no parsed lines', file=sys.stderr)
143 email.print_errors()
144 sys.exit(1)