git_email.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. #!/usr/bin/env python3
  2. #
  3. # This file is part of GCC.
  4. #
  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
  8. # version.
  9. #
  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
  13. # for more details.
  14. #
  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/>. */
  18. import os
  19. import re
  20. import sys
  21. from itertools import takewhile
  22. from dateutil.parser import parse
  23. from git_commit import GitCommit, GitInfo, decode_path
  24. from unidiff import PatchSet, PatchedFile
  25. DATE_PREFIX = 'Date: '
  26. FROM_PREFIX = 'From: '
  27. SUBJECT_PREFIX = 'Subject: '
  28. subject_patch_regex = re.compile(r'^\[PATCH( \d+/\d+)?\] ')
  29. unidiff_supports_renaming = hasattr(PatchedFile(), 'is_rename')
  30. class GitEmail(GitCommit):
  31. def __init__(self, filename):
  32. self.filename = filename
  33. diff = PatchSet.from_filename(filename)
  34. date = None
  35. author = None
  36. subject = ''
  37. subject_last = False
  38. with open(self.filename, 'r') as f:
  39. lines = f.read().splitlines()
  40. lines = list(takewhile(lambda line: line != '---', lines))
  41. for line in lines:
  42. if line.startswith(DATE_PREFIX):
  43. date = parse(line[len(DATE_PREFIX):])
  44. elif line.startswith(FROM_PREFIX):
  45. author = GitCommit.format_git_author(line[len(FROM_PREFIX):])
  46. elif line.startswith(SUBJECT_PREFIX):
  47. subject = line[len(SUBJECT_PREFIX):]
  48. subject_last = True
  49. elif subject_last and line.startswith(' '):
  50. subject += line
  51. elif line == '':
  52. break
  53. else:
  54. subject_last = False
  55. if subject:
  56. subject = subject_patch_regex.sub('', subject)
  57. header = list(takewhile(lambda line: line != '', lines))
  58. # Note: commit message consists of email subject, empty line, email body
  59. message = [subject] + lines[len(header):]
  60. modified_files = []
  61. for f in diff:
  62. # Strip "a/" and "b/" prefixes
  63. source = decode_path(f.source_file)[2:]
  64. target = decode_path(f.target_file)[2:]
  65. if f.is_added_file:
  66. t = 'A'
  67. elif f.is_removed_file:
  68. t = 'D'
  69. elif unidiff_supports_renaming and f.is_rename:
  70. # Consider that renamed files are two operations: the deletion
  71. # of the original name and the addition of the new one.
  72. modified_files.append((source, 'D'))
  73. t = 'A'
  74. else:
  75. t = 'M'
  76. modified_files.append((target if t != 'D' else source, t))
  77. git_info = GitInfo(None, date, author, message, modified_files)
  78. super().__init__(git_info,
  79. commit_to_info_hook=lambda x: None)
  80. def show_help():
  81. print("""usage: git_email.py [--help] [patch file ...]
  82. Check git ChangeLog format of a patch
  83. With zero arguments, process every patch file in the
  84. ./patches directory.
  85. With one argument, process the named patch file.
  86. Patch files must be in 'git format-patch' format.""")
  87. sys.exit(0)
  88. if __name__ == '__main__':
  89. if len(sys.argv) == 2 and (sys.argv[1] == '-h' or sys.argv[1] == '--help'):
  90. show_help()
  91. if len(sys.argv) == 1:
  92. allfiles = []
  93. for root, _dirs, files in os.walk('patches'):
  94. for f in files:
  95. full = os.path.join(root, f)
  96. allfiles.append(full)
  97. success = 0
  98. for full in sorted(allfiles):
  99. email = GitEmail(full, False)
  100. print(email.filename)
  101. if email.success:
  102. success += 1
  103. print(' OK')
  104. else:
  105. for error in email.errors:
  106. print(' ERR: %s' % error)
  107. print()
  108. print('Successfully parsed: %d/%d' % (success, len(allfiles)))
  109. else:
  110. email = GitEmail(sys.argv[1])
  111. if email.success:
  112. print('OK')
  113. email.print_output()
  114. else:
  115. if not email.info.lines:
  116. print('Error: patch contains no parsed lines', file=sys.stderr)
  117. email.print_errors()
  118. sys.exit(1)