Daily bump.
[official-gcc.git] / contrib / gcc-changelog / git_email.py
blob49f41f2ec9959b059a9ec78eed6733aeb2fe5b0b
1 #!/usr/bin/env python3
3 # Copyright (C) 2020-2023 Free Software Foundation, Inc.
5 # This file is part of GCC.
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.
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.
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/>. */
21 import os
22 import re
23 import sys
24 from itertools import takewhile
26 from dateutil.parser import parse
28 from git_commit import GitCommit, GitInfo, decode_path
30 from unidiff import PatchSet, PatchedFile
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')
39 class GitEmail(GitCommit):
40 def __init__(self, filename):
41 self.filename = filename
42 date = None
43 author = None
44 subject = ''
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
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):]
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:]
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,
93 commit_to_info_hook=lambda x: None)
96 def show_help():
97 print("""usage: git_email.py [--help] [patch file ...]
99 Check git ChangeLog format of a patch
101 With zero arguments, process every patch file in the
102 ./patches directory.
103 With one argument, process the named patch file.
105 Patch files must be in 'git format-patch' format.""")
106 sys.exit(0)
109 if __name__ == '__main__':
110 if len(sys.argv) == 2 and (sys.argv[1] == '-h' or sys.argv[1] == '--help'):
111 show_help()
113 if len(sys.argv) == 1:
114 allfiles = []
115 for root, _dirs, files in os.walk('patches'):
116 for f in files:
117 full = os.path.join(root, f)
118 allfiles.append(full)
120 success = 0
121 for full in sorted(allfiles):
122 email = GitEmail(full)
123 print(email.filename)
124 if email.success:
125 success += 1
126 print(' OK')
127 for warning in email.warnings:
128 print(' WARN: %s' % warning)
129 else:
130 for error in email.errors:
131 print(' ERR: %s' % error)
133 print()
134 print('Successfully parsed: %d/%d' % (success, len(allfiles)))
135 else:
136 email = GitEmail(sys.argv[1])
137 if email.success:
138 print('OK')
139 email.print_output()
140 email.print_warnings()
141 else:
142 if not email.info.lines:
143 print('Error: patch contains no parsed lines', file=sys.stderr)
144 email.print_errors()
145 sys.exit(1)