3 # This file is part of GCC.
5 # GCC is free software; you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation; either version 3, or (at your option) any later
10 # GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with GCC; see the file COPYING3. If not see
17 # <http://www.gnu.org/licenses/>. */
22 from itertools
import takewhile
24 from dateutil
.parser
import parse
26 from git_commit
import GitCommit
, GitInfo
, decode_path
28 from unidiff
import PatchSet
, PatchedFile
30 DATE_PREFIX
= 'Date: '
31 FROM_PREFIX
= 'From: '
32 SUBJECT_PREFIX
= 'Subject: '
33 subject_patch_regex
= re
.compile(r
'^\[PATCH( \d+/\d+)?\] ')
34 unidiff_supports_renaming
= hasattr(PatchedFile(), 'is_rename')
37 class GitEmail(GitCommit
):
38 def __init__(self
, filename
):
39 self
.filename
= filename
40 diff
= PatchSet
.from_filename(filename
)
46 with
open(self
.filename
, 'r') as f
:
47 lines
= f
.read().splitlines()
48 lines
= list(takewhile(lambda line
: line
!= '---', lines
))
50 if line
.startswith(DATE_PREFIX
):
51 date
= parse(line
[len(DATE_PREFIX
):])
52 elif line
.startswith(FROM_PREFIX
):
53 author
= GitCommit
.format_git_author(line
[len(FROM_PREFIX
):])
54 elif line
.startswith(SUBJECT_PREFIX
):
55 subject
= line
[len(SUBJECT_PREFIX
):]
57 elif subject_last
and line
.startswith(' '):
65 subject
= subject_patch_regex
.sub('', subject
)
66 header
= list(takewhile(lambda line
: line
!= '', lines
))
67 # Note: commit message consists of email subject, empty line, email body
68 message
= [subject
] + lines
[len(header
):]
72 # Strip "a/" and "b/" prefixes
73 source
= decode_path(f
.source_file
)[2:]
74 target
= decode_path(f
.target_file
)[2:]
78 elif f
.is_removed_file
:
80 elif unidiff_supports_renaming
and f
.is_rename
:
81 # Consider that renamed files are two operations: the deletion
82 # of the original name and the addition of the new one.
83 modified_files
.append((source
, 'D'))
87 modified_files
.append((target
if t
!= 'D' else source
, t
))
88 git_info
= GitInfo(None, date
, author
, message
, modified_files
)
89 super().__init
__(git_info
,
90 commit_to_info_hook
=lambda x
: None)
94 print("""usage: git_email.py [--help] [patch file ...]
96 Check git ChangeLog format of a patch
98 With zero arguments, process every patch file in the
100 With one argument, process the named patch file.
102 Patch files must be in 'git format-patch' format.""")
106 if __name__
== '__main__':
107 if len(sys
.argv
) == 2 and (sys
.argv
[1] == '-h' or sys
.argv
[1] == '--help'):
110 if len(sys
.argv
) == 1:
112 for root
, _dirs
, files
in os
.walk('patches'):
114 full
= os
.path
.join(root
, f
)
115 allfiles
.append(full
)
118 for full
in sorted(allfiles
):
119 email
= GitEmail(full
, False)
120 print(email
.filename
)
125 for error
in email
.errors
:
126 print(' ERR: %s' % error
)
129 print('Successfully parsed: %d/%d' % (success
, len(allfiles
)))
131 email
= GitEmail(sys
.argv
[1])
136 if not email
.info
.lines
:
137 print('Error: patch contains no parsed lines', file=sys
.stderr
)