| #!/usr/bin/env python3 |
| # |
| # This file is part of GCC. |
| # |
| # GCC is free software; you can redistribute it and/or modify it under |
| # the terms of the GNU General Public License as published by the Free |
| # Software Foundation; either version 3, or (at your option) any later |
| # version. |
| # |
| # GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| # WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| # for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with GCC; see the file COPYING3. If not see |
| # <http://www.gnu.org/licenses/>. */ |
| |
| import os |
| import re |
| import sys |
| from itertools import takewhile |
| |
| from dateutil.parser import parse |
| |
| from git_commit import GitCommit, GitInfo, decode_path |
| |
| from unidiff import PatchSet, PatchedFile |
| |
| DATE_PREFIX = 'Date: ' |
| FROM_PREFIX = 'From: ' |
| SUBJECT_PREFIX = 'Subject: ' |
| subject_patch_regex = re.compile(r'^\[PATCH( \d+/\d+)?\] ') |
| unidiff_supports_renaming = hasattr(PatchedFile(), 'is_rename') |
| |
| |
| class GitEmail(GitCommit): |
| def __init__(self, filename): |
| self.filename = filename |
| diff = PatchSet.from_filename(filename) |
| date = None |
| author = None |
| subject = '' |
| |
| subject_last = False |
| with open(self.filename, 'r') as f: |
| lines = f.read().splitlines() |
| lines = list(takewhile(lambda line: line != '---', lines)) |
| for line in lines: |
| if line.startswith(DATE_PREFIX): |
| date = parse(line[len(DATE_PREFIX):]) |
| elif line.startswith(FROM_PREFIX): |
| author = GitCommit.format_git_author(line[len(FROM_PREFIX):]) |
| elif line.startswith(SUBJECT_PREFIX): |
| subject = line[len(SUBJECT_PREFIX):] |
| subject_last = True |
| elif subject_last and line.startswith(' '): |
| subject += line |
| elif line == '': |
| break |
| else: |
| subject_last = False |
| |
| if subject: |
| subject = subject_patch_regex.sub('', subject) |
| header = list(takewhile(lambda line: line != '', lines)) |
| # Note: commit message consists of email subject, empty line, email body |
| message = [subject] + lines[len(header):] |
| |
| modified_files = [] |
| for f in diff: |
| # Strip "a/" and "b/" prefixes |
| source = decode_path(f.source_file)[2:] |
| target = decode_path(f.target_file)[2:] |
| |
| if f.is_added_file: |
| t = 'A' |
| elif f.is_removed_file: |
| t = 'D' |
| elif unidiff_supports_renaming and f.is_rename: |
| # Consider that renamed files are two operations: the deletion |
| # of the original name and the addition of the new one. |
| modified_files.append((source, 'D')) |
| t = 'A' |
| else: |
| t = 'M' |
| modified_files.append((target if t != 'D' else source, t)) |
| git_info = GitInfo(None, date, author, message, modified_files) |
| super().__init__(git_info, |
| commit_to_info_hook=lambda x: None) |
| |
| |
| def show_help(): |
| print("""usage: git_email.py [--help] [patch file ...] |
| |
| Check git ChangeLog format of a patch |
| |
| With zero arguments, process every patch file in the |
| ./patches directory. |
| With one argument, process the named patch file. |
| |
| Patch files must be in 'git format-patch' format.""") |
| sys.exit(0) |
| |
| |
| if __name__ == '__main__': |
| if len(sys.argv) == 2 and (sys.argv[1] == '-h' or sys.argv[1] == '--help'): |
| show_help() |
| |
| if len(sys.argv) == 1: |
| allfiles = [] |
| for root, _dirs, files in os.walk('patches'): |
| for f in files: |
| full = os.path.join(root, f) |
| allfiles.append(full) |
| |
| success = 0 |
| for full in sorted(allfiles): |
| email = GitEmail(full, False) |
| print(email.filename) |
| if email.success: |
| success += 1 |
| print(' OK') |
| else: |
| for error in email.errors: |
| print(' ERR: %s' % error) |
| |
| print() |
| print('Successfully parsed: %d/%d' % (success, len(allfiles))) |
| else: |
| email = GitEmail(sys.argv[1]) |
| if email.success: |
| print('OK') |
| email.print_output() |
| else: |
| if not email.info.lines: |
| print('Error: patch contains no parsed lines', file=sys.stderr) |
| email.print_errors() |
| sys.exit(1) |