diff options
author | Ian Lance Taylor <iant@golang.org> | 2021-09-13 10:37:49 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2021-09-13 10:37:49 -0700 |
commit | e252b51ccde010cbd2a146485d8045103cd99533 (patch) | |
tree | e060f101cdc32bf5e520de8e5275db9d4236b74c /contrib/gcc-changelog/git_commit.py | |
parent | f10c7c4596dda99d2ee872c995ae4aeda65adbdf (diff) | |
parent | 104c05c5284b7822d770ee51a7d91946c7e56d50 (diff) | |
download | gcc-e252b51ccde010cbd2a146485d8045103cd99533.zip gcc-e252b51ccde010cbd2a146485d8045103cd99533.tar.gz gcc-e252b51ccde010cbd2a146485d8045103cd99533.tar.bz2 |
Merge from trunk revision 104c05c5284b7822d770ee51a7d91946c7e56d50.
Diffstat (limited to 'contrib/gcc-changelog/git_commit.py')
-rwxr-xr-x | contrib/gcc-changelog/git_commit.py | 87 |
1 files changed, 67 insertions, 20 deletions
diff --git a/contrib/gcc-changelog/git_commit.py b/contrib/gcc-changelog/git_commit.py index 43fa7f4..d1646bd 100755 --- a/contrib/gcc-changelog/git_commit.py +++ b/contrib/gcc-changelog/git_commit.py @@ -19,8 +19,9 @@ import difflib import os import re +import sys -changelog_locations = { +default_changelog_locations = { 'c++tools', 'config', 'contrib', @@ -155,7 +156,9 @@ author_line_regex = \ re.compile(r'^(?P<datetime>\d{4}-\d{2}-\d{2})\ {2}(?P<name>.* <.*>)') additional_author_regex = re.compile(r'^\t(?P<spaces>\ *)?(?P<name>.* <.*>)') changelog_regex = re.compile(r'^(?:[fF]or +)?([a-z0-9+-/]*)ChangeLog:?') -pr_regex = re.compile(r'\tPR (?P<component>[a-z+-]+\/)?([0-9]+)$') +subject_pr_regex = re.compile(r'(^|\W)PR\s+(?P<component>[a-zA-Z+-]+)/(?P<pr>\d{4,7})') +subject_pr2_regex = re.compile(r'[(\[]PR\s*(?P<pr>\d{4,7})[)\]]') +pr_regex = re.compile(r'\tPR (?P<component>[a-z+-]+\/)?(?P<pr>[0-9]+)$') dr_regex = re.compile(r'\tDR ([0-9]+)$') star_prefix_regex = re.compile(r'\t\*(?P<spaces>\ *)(?P<content>.*)') end_of_location_regex = re.compile(r'[\[<(:]') @@ -200,7 +203,7 @@ class Error: def __repr__(self): s = self.message if self.line: - s += ':"%s"' % self.line + s += ': "%s"' % self.line return s @@ -287,7 +290,7 @@ class GitInfo: class GitCommit: - def __init__(self, info, strict=True, commit_to_info_hook=None): + def __init__(self, info, commit_to_info_hook=None, ref_name=None): self.original_info = info self.info = info self.message = None @@ -297,14 +300,19 @@ class GitCommit: self.top_level_authors = [] self.co_authors = [] self.top_level_prs = [] + self.subject_prs = set() self.cherry_pick_commit = None self.revert_commit = None self.commit_to_info_hook = commit_to_info_hook + self.init_changelog_locations(ref_name) # Skip Update copyright years commits if self.info.lines and self.info.lines[0] == 'Update copyright years.': return + if self.info.lines and len(self.info.lines) > 1 and self.info.lines[1]: + self.errors.append(Error('Expected empty second line in commit message', info.lines[0])) + # Identify first if the commit is a Revert commit for line in self.info.lines: m = revert_regex.match(line) @@ -314,6 +322,19 @@ class GitCommit: if self.revert_commit: self.info = self.commit_to_info_hook(self.revert_commit) + # The following happens for get_email.py: + if not self.info: + return + + # Extract PR numbers form the subject line + # Match either [PRnnnn] / (PRnnnn) or PR component/nnnn + if self.info.lines and not self.revert_commit: + self.subject_prs = {m.group('pr') for m in subject_pr2_regex.finditer(info.lines[0])} + for m in subject_pr_regex.finditer(info.lines[0]): + if not m.group('component') in bug_components: + self.errors.append(Error('invalid PR component in subject', info.lines[0])) + self.subject_prs.add(m.group('pr')) + # Allow complete deletion of ChangeLog files in a commit project_files = [f for f in self.info.modified_files if (self.is_changelog_filename(f[0], allow_suffix=True) and f[1] != 'D') @@ -323,10 +344,12 @@ class GitCommit: if len(project_files) == len(self.info.modified_files): # All modified files are only MISC files return - elif project_files and strict: - self.errors.append(Error('ChangeLog, DATESTAMP, BASE-VER and ' - 'DEV-PHASE updates should be done ' - 'separately from normal commits')) + elif project_files: + err = 'ChangeLog, DATESTAMP, BASE-VER and DEV-PHASE updates ' \ + 'should be done separately from normal commits\n' \ + '(note: ChangeLog entries will be automatically ' \ + 'added by a cron job)' + self.errors.append(Error(err)) return all_are_ignored = (len(project_files) + len(ignored_files) @@ -342,6 +365,9 @@ class GitCommit: if not self.errors: self.check_mentioned_files() self.check_for_correct_changelog() + if self.subject_prs: + self.errors.append(Error('PR %s in subject but not in changelog' % + ', '.join(self.subject_prs), self.info.lines[0])) @property def success(self): @@ -361,15 +387,14 @@ class GitCommit: else: return False - @classmethod - def find_changelog_location(cls, name): + def find_changelog_location(self, name): if name.startswith('\t'): name = name[1:] if name.endswith(':'): name = name[:-1] if name.endswith('/'): name = name[:-1] - return name if name in changelog_locations else None + return name if name in self.changelog_locations else None @classmethod def format_git_author(cls, author): @@ -389,6 +414,17 @@ class GitCommit: modified_files.append((parts[2], 'A')) return modified_files + def init_changelog_locations(self, ref_name): + self.changelog_locations = list(default_changelog_locations) + if ref_name: + version = sys.maxsize + if 'releases/gcc-' in ref_name: + version = int(ref_name.split('-')[-1]) + if version >= 12: + # HSA and BRIG were removed in GCC 12 + self.changelog_locations.remove('gcc/brig') + self.changelog_locations.remove('libhsail-rt') + def parse_lines(self, all_are_ignored): body = self.info.lines @@ -397,7 +433,8 @@ class GitCommit: continue if (changelog_regex.match(b) or self.find_changelog_location(b) or star_prefix_regex.match(b) or pr_regex.match(b) - or dr_regex.match(b) or author_line_regex.match(b)): + or dr_regex.match(b) or author_line_regex.match(b) + or b.lower().startswith(CO_AUTHORED_BY_PREFIX)): self.changes = body[i:] return if not all_are_ignored: @@ -415,8 +452,10 @@ class GitCommit: if line != line.rstrip(): self.errors.append(Error('trailing whitespace', line)) if len(line.replace('\t', ' ' * TAB_WIDTH)) > LINE_LIMIT: - self.errors.append(Error('line exceeds %d character limit' - % LINE_LIMIT, line)) + # support long filenames + if not line.startswith('\t* ') or not line.endswith(':') or ' ' in line[3:-1]: + self.errors.append(Error('line exceeds %d character limit' + % LINE_LIMIT, line)) m = changelog_regex.match(line) if m: last_entry = ChangeLogEntry(m.group(1).rstrip('/'), @@ -443,7 +482,9 @@ class GitCommit: else: author_tuple = (m.group('name'), None) elif pr_regex.match(line): - component = pr_regex.match(line).group('component') + m = pr_regex.match(line) + component = m.group('component') + pr = m.group('pr') if not component: self.errors.append(Error('missing PR component', line)) continue @@ -452,6 +493,8 @@ class GitCommit: continue else: pr_line = line.lstrip() + if pr in self.subject_prs: + self.subject_prs.remove(pr) elif dr_regex.match(line): pr_line = line.lstrip() @@ -584,7 +627,7 @@ class GitCommit: for file in entry.files: location = self.get_file_changelog_location(file) if (location == '' - or (location and location in changelog_locations)): + or (location and location in self.changelog_locations)): if changelog and changelog != location: msg = 'could not deduce ChangeLog file, ' \ 'not unique location' @@ -604,11 +647,10 @@ class GitCommit: return True return False - @classmethod - def get_changelog_by_path(cls, path): + def get_changelog_by_path(self, path): components = path.split('/') while components: - if '/'.join(components) in changelog_locations: + if '/'.join(components) in self.changelog_locations: break components = components[:-1] return '/'.join(components) @@ -627,7 +669,12 @@ class GitCommit: assert not entry.folder.endswith('/') for file in entry.files: if not self.is_changelog_filename(file): - mentioned_files.add(os.path.join(entry.folder, file)) + item = os.path.join(entry.folder, file) + if item in mentioned_files: + msg = 'same file specified multiple times' + self.errors.append(Error(msg, file)) + else: + mentioned_files.add(item) for pattern in entry.file_patterns: mentioned_patterns.append(os.path.join(entry.folder, pattern)) |