(root)/
gcc-13.2.0/
contrib/
gcc-changelog/
git_email.py
       1  #!/usr/bin/env python3
       2  
       3  # Copyright (C) 2020-2023 Free Software Foundation, Inc.
       4  #
       5  # This file is part of GCC.
       6  #
       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
      10  # version.
      11  #
      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
      15  # for more details.
      16  #
      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/>.  */
      20  
      21  import os
      22  import re
      23  import sys
      24  from itertools import takewhile
      25  
      26  from dateutil.parser import parse
      27  
      28  from git_commit import GitCommit, GitInfo, decode_path
      29  
      30  from unidiff import PatchSet, PatchedFile
      31  
      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')
      37  
      38  
      39  class ESC[4;38;5;81mGitEmail(ESC[4;38;5;149mGitCommit):
      40      def __init__(self, filename):
      41          self.filename = filename
      42          date = None
      43          author = None
      44          subject = ''
      45  
      46          subject_last = False
      47          with open(self.filename, newline='\n') as f:
      48              data = f.read()
      49              diff = PatchSet(data)
      50              lines = data.splitlines()
      51          lines = list(takewhile(lambda line: line != '---', lines))
      52          for line in 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):]
      59                  subject_last = True
      60              elif subject_last and line.startswith(' '):
      61                  subject += line
      62              elif line == '':
      63                  break
      64              else:
      65                  subject_last = False
      66  
      67          if subject:
      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):]
      72  
      73          modified_files = []
      74          for f in diff:
      75              # Strip "a/" and "b/" prefixes
      76              source = decode_path(f.source_file)[2:]
      77              target = decode_path(f.target_file)[2:]
      78  
      79              if f.is_added_file:
      80                  t = 'A'
      81              elif f.is_removed_file:
      82                  t = 'D'
      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'))
      87                  t = 'A'
      88              else:
      89                  t = 'M'
      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)
      94  
      95  
      96  def show_help():
      97      print("""usage: git_email.py [--help] [patch file ...]
      98  
      99  Check git ChangeLog format of a patch
     100  
     101  With zero arguments, process every patch file in the
     102  ./patches directory.
     103  With one argument, process the named patch file.
     104  
     105  Patch files must be in 'git format-patch' format.""")
     106      sys.exit(0)
     107  
     108  
     109  if __name__ == '__main__':
     110      if len(sys.argv) == 2 and (sys.argv[1] == '-h' or sys.argv[1] == '--help'):
     111          show_help()
     112  
     113      if len(sys.argv) == 1:
     114          allfiles = []
     115          for root, _dirs, files in os.walk('patches'):
     116              for f in files:
     117                  full = os.path.join(root, f)
     118                  allfiles.append(full)
     119  
     120          success = 0
     121          for full in sorted(allfiles):
     122              email = GitEmail(full)
     123              print(email.filename)
     124              if email.success:
     125                  success += 1
     126                  print('  OK')
     127                  for warning in email.warnings:
     128                      print('  WARN: %s' % warning)
     129              else:
     130                  for error in email.errors:
     131                      print('  ERR: %s' % error)
     132  
     133          print()
     134          print('Successfully parsed: %d/%d' % (success, len(allfiles)))
     135      else:
     136          email = GitEmail(sys.argv[1])
     137          if email.success:
     138              print('OK')
     139              email.print_output()
     140              email.print_warnings()
     141          else:
     142              if not email.info.lines:
     143                  print('Error: patch contains no parsed lines', file=sys.stderr)
     144              email.print_errors()
     145              sys.exit(1)