3 # Copyright (C) 2020-2024 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
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
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/>.
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
47 with
open(self
.filename
, newline
='\n') as f
:
50 lines
= data
.splitlines()
51 lines
= list(takewhile(lambda line
: line
!= '---', 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
):]
60 elif subject_last
and line
.startswith(' '):
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
):]
75 # Strip "a/" and "b/" prefixes
76 source
= decode_path(f
.source_file
)[2:]
77 target
= decode_path(f
.target_file
)[2:]
81 elif f
.is_removed_file
:
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'))
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)
96 print("""usage: git_email.py [--help] [patch file ...]
98 Check git ChangeLog format of a patch
100 With zero arguments, process every patch file in the
102 With one argument, process the named patch file.
104 Patch files must be in 'git format-patch' format.""")
108 if __name__
== '__main__':
109 if len(sys
.argv
) == 2 and (sys
.argv
[1] == '-h' or sys
.argv
[1] == '--help'):
112 if len(sys
.argv
) == 1:
114 for root
, _dirs
, files
in os
.walk('patches'):
116 full
= os
.path
.join(root
, f
)
117 allfiles
.append(full
)
120 for full
in sorted(allfiles
):
121 email
= GitEmail(full
)
122 print(email
.filename
)
126 for warning
in email
.warnings
:
127 print(' WARN: %s' % warning
)
129 for error
in email
.errors
:
130 print(' ERR: %s' % error
)
133 print('Successfully parsed: %d/%d' % (success
, len(allfiles
)))
135 email
= GitEmail(sys
.argv
[1])
139 email
.print_warnings()
141 if not email
.info
.lines
:
142 print('Error: patch contains no parsed lines', file=sys
.stderr
)