3 # Copyright (C) 2020 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
42 diff
= PatchSet
.from_filename(filename
, newline
='\n')
48 with
open(self
.filename
, 'r') as f
:
49 lines
= f
.read().splitlines()
50 lines
= list(takewhile(lambda line
: line
!= '---', lines
))
52 if line
.startswith(DATE_PREFIX
):
53 date
= parse(line
[len(DATE_PREFIX
):])
54 elif line
.startswith(FROM_PREFIX
):
55 author
= GitCommit
.format_git_author(line
[len(FROM_PREFIX
):])
56 elif line
.startswith(SUBJECT_PREFIX
):
57 subject
= line
[len(SUBJECT_PREFIX
):]
59 elif subject_last
and line
.startswith(' '):
67 subject
= subject_patch_regex
.sub('', subject
)
68 header
= list(takewhile(lambda line
: line
!= '', lines
))
69 # Note: commit message consists of email subject, empty line, email body
70 message
= [subject
] + lines
[len(header
):]
74 # Strip "a/" and "b/" prefixes
75 source
= decode_path(f
.source_file
)[2:]
76 target
= decode_path(f
.target_file
)[2:]
80 elif f
.is_removed_file
:
82 elif unidiff_supports_renaming
and f
.is_rename
:
83 # Consider that renamed files are two operations: the deletion
84 # of the original name and the addition of the new one.
85 modified_files
.append((source
, 'D'))
89 modified_files
.append((target
if t
!= 'D' else source
, t
))
90 git_info
= GitInfo(None, date
, author
, message
, modified_files
)
91 super().__init
__(git_info
,
92 commit_to_info_hook
=lambda x
: 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
, False)
122 print(email
.filename
)
127 for error
in email
.errors
:
128 print(' ERR: %s' % error
)
131 print('Successfully parsed: %d/%d' % (success
, len(allfiles
)))
133 email
= GitEmail(sys
.argv
[1])
138 if not email
.info
.lines
:
139 print('Error: patch contains no parsed lines', file=sys
.stderr
)