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
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
,
93 commit_to_info_hook
=lambda x
: None)
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
103 With one argument, process the named patch file.
105 Patch files must be in 'git format-patch' format.""")
109 if __name__
== '__main__':
110 if len(sys
.argv
) == 2 and (sys
.argv
[1] == '-h' or sys
.argv
[1] == '--help'):
113 if len(sys
.argv
) == 1:
115 for root
, _dirs
, files
in os
.walk('patches'):
117 full
= os
.path
.join(root
, f
)
118 allfiles
.append(full
)
121 for full
in sorted(allfiles
):
122 email
= GitEmail(full
)
123 print(email
.filename
)
127 for warning
in email
.warnings
:
128 print(' WARN: %s' % warning
)
130 for error
in email
.errors
:
131 print(' ERR: %s' % error
)
134 print('Successfully parsed: %d/%d' % (success
, len(allfiles
)))
136 email
= GitEmail(sys
.argv
[1])
140 email
.print_warnings()
142 if not email
.info
.lines
:
143 print('Error: patch contains no parsed lines', file=sys
.stderr
)