]>
Commit | Line | Data |
---|---|---|
c10aa1f0 | 1 | #!/usr/bin/env python3 |
81f86cb9 ML |
2 | |
3 | # Copyright (C) 2020 Free Software Foundation, Inc. | |
c10aa1f0 ML |
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 | |
d554f43c | 22 | import re |
c10aa1f0 ML |
23 | import sys |
24 | from itertools import takewhile | |
25 | ||
26 | from dateutil.parser import parse | |
27 | ||
57706dd7 | 28 | from git_commit import GitCommit, GitInfo, decode_path |
c10aa1f0 | 29 | |
84ed8d2c | 30 | from unidiff import PatchSet, PatchedFile |
c10aa1f0 ML |
31 | |
32 | DATE_PREFIX = 'Date: ' | |
33 | FROM_PREFIX = 'From: ' | |
d554f43c TB |
34 | SUBJECT_PREFIX = 'Subject: ' |
35 | subject_patch_regex = re.compile(r'^\[PATCH( \d+/\d+)?\] ') | |
84ed8d2c | 36 | unidiff_supports_renaming = hasattr(PatchedFile(), 'is_rename') |
c10aa1f0 ML |
37 | |
38 | ||
39 | class GitEmail(GitCommit): | |
8f67bf25 | 40 | def __init__(self, filename): |
c10aa1f0 | 41 | self.filename = filename |
8f2a8be5 TB |
42 | try: |
43 | diff = PatchSet.from_filename(filename, newline='\n') | |
44 | except TypeError: | |
45 | # Older versions don't have the newline argument | |
46 | diff = PatchSet.from_filename(filename) | |
c10aa1f0 ML |
47 | date = None |
48 | author = None | |
d554f43c | 49 | subject = '' |
c10aa1f0 | 50 | |
d554f43c | 51 | subject_last = False |
bb07057a PMR |
52 | with open(self.filename, 'r') as f: |
53 | lines = f.read().splitlines() | |
c10aa1f0 ML |
54 | lines = list(takewhile(lambda line: line != '---', lines)) |
55 | for line in lines: | |
56 | if line.startswith(DATE_PREFIX): | |
57 | date = parse(line[len(DATE_PREFIX):]) | |
58 | elif line.startswith(FROM_PREFIX): | |
59 | author = GitCommit.format_git_author(line[len(FROM_PREFIX):]) | |
d554f43c TB |
60 | elif line.startswith(SUBJECT_PREFIX): |
61 | subject = line[len(SUBJECT_PREFIX):] | |
62 | subject_last = True | |
63 | elif subject_last and line.startswith(' '): | |
64 | subject += line | |
65 | elif line == '': | |
66 | break | |
67 | else: | |
68 | subject_last = False | |
69 | ||
70 | if subject: | |
71 | subject = subject_patch_regex.sub('', subject) | |
c10aa1f0 | 72 | header = list(takewhile(lambda line: line != '', lines)) |
d554f43c TB |
73 | # Note: commit message consists of email subject, empty line, email body |
74 | message = [subject] + lines[len(header):] | |
c10aa1f0 ML |
75 | |
76 | modified_files = [] | |
77 | for f in diff: | |
a634157d | 78 | # Strip "a/" and "b/" prefixes |
57706dd7 ML |
79 | source = decode_path(f.source_file)[2:] |
80 | target = decode_path(f.target_file)[2:] | |
a634157d | 81 | |
c10aa1f0 ML |
82 | if f.is_added_file: |
83 | t = 'A' | |
84 | elif f.is_removed_file: | |
85 | t = 'D' | |
84ed8d2c | 86 | elif unidiff_supports_renaming and f.is_rename: |
a634157d PMR |
87 | # Consider that renamed files are two operations: the deletion |
88 | # of the original name and the addition of the new one. | |
89 | modified_files.append((source, 'D')) | |
90 | t = 'A' | |
c10aa1f0 ML |
91 | else: |
92 | t = 'M' | |
a7ac3e92 | 93 | modified_files.append((target if t != 'D' else source, t)) |
d554f43c | 94 | git_info = GitInfo(None, date, author, message, modified_files) |
8f67bf25 | 95 | super().__init__(git_info, |
de4676c9 | 96 | commit_to_info_hook=lambda x: None) |
c10aa1f0 ML |
97 | |
98 | ||
8300c346 | 99 | def show_help(): |
0fd1a54b | 100 | print("""usage: git_email.py [--help] [patch file ...] |
8300c346 TB |
101 | |
102 | Check git ChangeLog format of a patch | |
103 | ||
104 | With zero arguments, process every patch file in the | |
105 | ./patches directory. | |
106 | With one argument, process the named patch file. | |
107 | ||
0fd1a54b | 108 | Patch files must be in 'git format-patch' format.""") |
8300c346 TB |
109 | sys.exit(0) |
110 | ||
111 | ||
c10aa1f0 | 112 | if __name__ == '__main__': |
8300c346 TB |
113 | if len(sys.argv) == 2 and (sys.argv[1] == '-h' or sys.argv[1] == '--help'): |
114 | show_help() | |
115 | ||
c10aa1f0 ML |
116 | if len(sys.argv) == 1: |
117 | allfiles = [] | |
118 | for root, _dirs, files in os.walk('patches'): | |
119 | for f in files: | |
120 | full = os.path.join(root, f) | |
121 | allfiles.append(full) | |
122 | ||
123 | success = 0 | |
124 | for full in sorted(allfiles): | |
125 | email = GitEmail(full, False) | |
126 | print(email.filename) | |
127 | if email.success: | |
128 | success += 1 | |
129 | print(' OK') | |
130 | else: | |
131 | for error in email.errors: | |
132 | print(' ERR: %s' % error) | |
133 | ||
134 | print() | |
135 | print('Successfully parsed: %d/%d' % (success, len(allfiles))) | |
136 | else: | |
7b4bae0a | 137 | email = GitEmail(sys.argv[1]) |
c10aa1f0 ML |
138 | if email.success: |
139 | print('OK') | |
140 | email.print_output() | |
141 | else: | |
de4676c9 | 142 | if not email.info.lines: |
c10aa1f0 ML |
143 | print('Error: patch contains no parsed lines', file=sys.stderr) |
144 | email.print_errors() | |
62963c60 | 145 | sys.exit(1) |