Hello.

After a brief discussion with Jakub, I was convinced to support 'git revert' 
where
a ChangeLog is created from the commit the was reverted.

One example:

$ git gcc-verify 2635f9e5086318f4560997d9741fdda496b9c801 -p
Checking 2635f9e5086318f4560997d9741fdda496b9c801: OK
------ libstdc++-v3/ChangeLog ------
2020-06-29  Ville Voutilainen  <ville.voutilai...@gmail.com>

        Revert:
        2020-06-28  Ville Voutilainen  <ville.voutilai...@gmail.com>

        * include/bits/basic_string.h (string(_CharT*, const _Alloc&)):
        Add a __nonnull__ attribute.
        * testsuite/21_strings/basic_string/cons/char/nonnull.cc: New.
        * testsuite/21_strings/basic_string/cons/wchar_t/nonnull.cc: Likewise.

Where original message looks like:

commit 2635f9e5086318f4560997d9741fdda496b9c801
Author:     Ville Voutilainen <ville.voutilai...@gmail.com>
AuthorDate: Tue Jun 30 01:59:34 2020 +0300
Commit:     Ville Voutilainen <ville.voutilai...@gmail.com>
CommitDate: Tue Jun 30 01:59:34 2020 +0300

    Revert "Add a __nonnnull__ attribute to std::string's _CharT* constructor"
This reverts commit b26fd416fb0a734d3f3e56629b6dff2e3c25dd40.

I'm sending 2 patches where the first one is about a refactoring (creation of 
GitInfo class).
And we'll also need to update git-hooks (use the GitInfo class).

And I checked that 'git gcc-verify HEAD~300..HEAD -p' still looks fine.

Martin
>From 68e15be455ebde3cbb5815f72daf14d68246d6b3 Mon Sep 17 00:00:00 2001
From: Martin Liska <mli...@suse.cz>
Date: Tue, 30 Jun 2020 10:40:47 +0200
Subject: [PATCH] Use new GitInfo wrapper class.

---
 hooks/pre_commit_checks.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/hooks/pre_commit_checks.py b/hooks/pre_commit_checks.py
index fe51dca..fb00896 100644
--- a/hooks/pre_commit_checks.py
+++ b/hooks/pre_commit_checks.py
@@ -3,7 +3,7 @@ from pipes import quote
 import re
 from subprocess import Popen, PIPE, STDOUT
 
-from git_commit import GitCommit
+from git_commit import GitCommit, GitInfo
 from datetime import datetime
 
 from config import git_config
@@ -335,8 +335,9 @@ def verify_changelog_format(rev, raw_body):
     changed_files = git.diff('%s~..%s' % (rev, rev), name_status=True)
 
     date = datetime.utcfromtimestamp(int(committed_date))
-    git_commit = GitCommit(date, rev, author, raw_body,
-                           GitCommit.parse_git_name_status(changed_files))
+    git_info = GitInfo(rev, date, author, raw_body,
+                       GitCommit.parse_git_name_status(changed_files))
+    git_commit = GitCommit(git_info)
 
     if git_commit.success:
         # OK
-- 
2.27.0

>From e91dae973b00ea36d31f890b5992e10d86e73096 Mon Sep 17 00:00:00 2001
From: Martin Liska <mli...@suse.cz>
Date: Tue, 30 Jun 2020 10:32:34 +0200
Subject: [PATCH 2/2] gcc-changelog: support 'This revert commit' prefix.

contrib/ChangeLog:

	* gcc-changelog/git_check_commit.py: Print revision
	of original_info.
	* gcc-changelog/git_commit.py: Support Revert commits.
---
 contrib/gcc-changelog/git_check_commit.py |  2 +-
 contrib/gcc-changelog/git_commit.py       | 22 ++++++++++++++++++++--
 2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/contrib/gcc-changelog/git_check_commit.py b/contrib/gcc-changelog/git_check_commit.py
index ab6da05744a..935425ef813 100755
--- a/contrib/gcc-changelog/git_check_commit.py
+++ b/contrib/gcc-changelog/git_check_commit.py
@@ -37,7 +37,7 @@ retval = 0
 for git_commit in parse_git_revisions(args.git_path, args.revisions,
                                       not args.non_strict_mode):
     res = 'OK' if git_commit.success else 'FAILED'
-    print('Checking %s: %s' % (git_commit.info.hexsha, res))
+    print('Checking %s: %s' % (git_commit.original_info.hexsha, res))
     if git_commit.success:
         if args.print_changelog:
             git_commit.print_output()
diff --git a/contrib/gcc-changelog/git_commit.py b/contrib/gcc-changelog/git_commit.py
index 9d821a8940d..4d003ccf496 100755
--- a/contrib/gcc-changelog/git_commit.py
+++ b/contrib/gcc-changelog/git_commit.py
@@ -159,6 +159,7 @@ LINE_LIMIT = 100
 TAB_WIDTH = 8
 CO_AUTHORED_BY_PREFIX = 'co-authored-by: '
 CHERRY_PICK_PREFIX = '(cherry picked from commit '
+REVERT_PREFIX = 'This reverts commit '
 
 REVIEW_PREFIXES = ('reviewed-by: ', 'reviewed-on: ', 'signed-off-by: ',
                    'acked-by: ', 'tested-by: ', 'reported-by: ',
@@ -256,6 +257,7 @@ class GitInfo:
 
 class GitCommit:
     def __init__(self, info, strict=True, commit_to_info_hook=None):
+        self.original_info = info
         self.info = info
         self.message = None
         self.changes = None
@@ -265,8 +267,17 @@ class GitCommit:
         self.co_authors = []
         self.top_level_prs = []
         self.cherry_pick_commit = None
+        self.revert_commit = None
         self.commit_to_info_hook = commit_to_info_hook
 
+        # Identify first if the commit is a Revert commit
+        for line in self.info.lines:
+            if line.startswith(REVERT_PREFIX):
+                self.revert_commit = line[len(REVERT_PREFIX):].rstrip('.')
+                break
+        if self.revert_commit:
+            self.info = self.commit_to_info_hook(self.revert_commit)
+
         project_files = [f for f in self.info.modified_files
                          if self.is_changelog_filename(f[0])
                          or f[0] in misc_files]
@@ -625,6 +636,10 @@ class GitCommit:
                     timestamp = info.date.strftime(DATE_FORMAT)
                 else:
                     timestamp = current_timestamp
+            elif self.revert_commit:
+                timestamp = current_timestamp
+                orig_date = self.original_info.date
+                current_timestamp = orig_date.strftime(DATE_FORMAT)
             elif not timestamp or use_commit_ts:
                 timestamp = current_timestamp
             authors = entry.authors if entry.authors else [self.info.author]
@@ -633,10 +648,13 @@ class GitCommit:
                 if author not in authors:
                     authors.append(author)
 
-            if self.cherry_pick_commit:
+            if self.cherry_pick_commit or self.revert_commit:
                 output += self.format_authors_in_changelog([self.info.author],
                                                            current_timestamp)
-                output += '\tBackported from master:\n'
+                if self.cherry_pick_commit:
+                    output += '\tBackported from master:\n'
+                else:
+                    output += '\tRevert:\n'
                 output += self.format_authors_in_changelog(authors,
                                                            timestamp, '\t')
             else:
-- 
2.27.0

>From 4e6b10d9503fe399647f203b817061e2e0df6133 Mon Sep 17 00:00:00 2001
From: Martin Liska <mli...@suse.cz>
Date: Tue, 30 Jun 2020 10:12:45 +0200
Subject: [PATCH 1/2] gcc-changelog: come up with GitInfo wrapper.

contrib/ChangeLog:

	* gcc-changelog/git_check_commit.py: Use GitInfo
	* gcc-changelog/git_commit.py: Add GitInfo class.
	* gcc-changelog/git_email.py: Use GitInfo class.
	* gcc-changelog/git_repository.py: Likewise.
---
 contrib/gcc-changelog/git_check_commit.py |  2 +-
 contrib/gcc-changelog/git_commit.py       | 46 +++++++++++---------
 contrib/gcc-changelog/git_email.py        |  9 ++--
 contrib/gcc-changelog/git_repository.py   | 53 ++++++++++++-----------
 4 files changed, 58 insertions(+), 52 deletions(-)

diff --git a/contrib/gcc-changelog/git_check_commit.py b/contrib/gcc-changelog/git_check_commit.py
index 2601ae4f613..ab6da05744a 100755
--- a/contrib/gcc-changelog/git_check_commit.py
+++ b/contrib/gcc-changelog/git_check_commit.py
@@ -37,7 +37,7 @@ retval = 0
 for git_commit in parse_git_revisions(args.git_path, args.revisions,
                                       not args.non_strict_mode):
     res = 'OK' if git_commit.success else 'FAILED'
-    print('Checking %s: %s' % (git_commit.hexsha, res))
+    print('Checking %s: %s' % (git_commit.info.hexsha, res))
     if git_commit.success:
         if args.print_changelog:
             git_commit.print_output()
diff --git a/contrib/gcc-changelog/git_commit.py b/contrib/gcc-changelog/git_commit.py
index 4a78793b87e..9d821a8940d 100755
--- a/contrib/gcc-changelog/git_commit.py
+++ b/contrib/gcc-changelog/git_commit.py
@@ -245,30 +245,34 @@ class ChangeLogEntry:
         return False
 
 
-class GitCommit:
-    def __init__(self, hexsha, date, author, body, modified_files,
-                 strict=True, commit_to_date_hook=None):
+class GitInfo:
+    def __init__(self, hexsha, date, author, lines, modified_files):
         self.hexsha = hexsha
-        self.lines = body
+        self.date = date
+        self.author = author
+        self.lines = lines
         self.modified_files = modified_files
+
+
+class GitCommit:
+    def __init__(self, info, strict=True, commit_to_info_hook=None):
+        self.info = info
         self.message = None
         self.changes = None
         self.changelog_entries = []
         self.errors = []
-        self.date = date
-        self.author = author
         self.top_level_authors = []
         self.co_authors = []
         self.top_level_prs = []
         self.cherry_pick_commit = None
-        self.commit_to_date_hook = commit_to_date_hook
+        self.commit_to_info_hook = commit_to_info_hook
 
-        project_files = [f for f in self.modified_files
+        project_files = [f for f in self.info.modified_files
                          if self.is_changelog_filename(f[0])
                          or f[0] in misc_files]
-        ignored_files = [f for f in self.modified_files
+        ignored_files = [f for f in self.info.modified_files
                          if self.in_ignored_location(f[0])]
-        if len(project_files) == len(self.modified_files):
+        if len(project_files) == len(self.info.modified_files):
             # All modified files are only MISC files
             return
         elif project_files and strict:
@@ -278,7 +282,7 @@ class GitCommit:
             return
 
         all_are_ignored = (len(project_files) + len(ignored_files)
-                           == len(self.modified_files))
+                           == len(self.info.modified_files))
         self.parse_lines(all_are_ignored)
         if self.changes:
             self.parse_changelog()
@@ -296,7 +300,7 @@ class GitCommit:
 
     @property
     def new_files(self):
-        return [x[0] for x in self.modified_files if x[1] == 'A']
+        return [x[0] for x in self.info.modified_files if x[1] == 'A']
 
     @classmethod
     def is_changelog_filename(cls, path):
@@ -331,7 +335,7 @@ class GitCommit:
         return modified_files
 
     def parse_lines(self, all_are_ignored):
-        body = self.lines
+        body = self.info.lines
 
         for i, b in enumerate(body):
             if not b:
@@ -475,7 +479,7 @@ class GitCommit:
                     self.errors.append(Error(msg, line))
 
     def get_file_changelog_location(self, changelog_file):
-        for file in self.modified_files:
+        for file in self.info.modified_files:
             if file[0] == changelog_file:
                 # root ChangeLog file
                 return ''
@@ -538,7 +542,7 @@ class GitCommit:
             for pattern in entry.file_patterns:
                 mentioned_patterns.append(os.path.join(entry.folder, pattern))
 
-        cand = [x[0] for x in self.modified_files
+        cand = [x[0] for x in self.info.modified_files
                 if not self.is_changelog_filename(x[0])]
         changed_files = set(cand)
         for file in sorted(mentioned_files - changed_files):
@@ -609,28 +613,28 @@ class GitCommit:
         return output
 
     def to_changelog_entries(self, use_commit_ts=False):
-        current_timestamp = self.date.strftime(DATE_FORMAT)
+        current_timestamp = self.info.date.strftime(DATE_FORMAT)
         for entry in self.changelog_entries:
             output = ''
             timestamp = entry.datetime
             if self.cherry_pick_commit:
-                timestamp = self.commit_to_date_hook(self.cherry_pick_commit)
+                info = self.commit_to_info_hook(self.cherry_pick_commit)
                 # it can happen that it is a cherry-pick for a different
                 # repository
-                if timestamp:
-                    timestamp = timestamp.strftime(DATE_FORMAT)
+                if info:
+                    timestamp = info.date.strftime(DATE_FORMAT)
                 else:
                     timestamp = current_timestamp
             elif not timestamp or use_commit_ts:
                 timestamp = current_timestamp
-            authors = entry.authors if entry.authors else [self.author]
+            authors = entry.authors if entry.authors else [self.info.author]
             # add Co-Authored-By authors to all ChangeLog entries
             for author in self.co_authors:
                 if author not in authors:
                     authors.append(author)
 
             if self.cherry_pick_commit:
-                output += self.format_authors_in_changelog([self.author],
+                output += self.format_authors_in_changelog([self.info.author],
                                                            current_timestamp)
                 output += '\tBackported from master:\n'
                 output += self.format_authors_in_changelog(authors,
diff --git a/contrib/gcc-changelog/git_email.py b/contrib/gcc-changelog/git_email.py
index 2083d7b7ec9..014fdd1004b 100755
--- a/contrib/gcc-changelog/git_email.py
+++ b/contrib/gcc-changelog/git_email.py
@@ -22,7 +22,7 @@ from itertools import takewhile
 
 from dateutil.parser import parse
 
-from git_commit import GitCommit
+from git_commit import GitCommit, GitInfo
 
 from unidiff import PatchSet
 
@@ -66,8 +66,9 @@ class GitEmail(GitCommit):
             else:
                 t = 'M'
             modified_files.append((target, t))
-        super().__init__(None, date, author, body, modified_files,
-                         strict=strict, commit_to_date_hook=lambda x: date)
+        git_info = GitInfo(None, date, author, body, modified_files)
+        super().__init__(git_info, strict=strict,
+                         commit_to_info_hook=lambda x: None)
 
 
 # With zero arguments, process every patch file in the ./patches directory.
@@ -100,7 +101,7 @@ if __name__ == '__main__':
             print('OK')
             email.print_output()
         else:
-            if not email.lines:
+            if not email.info.lines:
                 print('Error: patch contains no parsed lines', file=sys.stderr)
             email.print_errors()
             sys.exit(1)
diff --git a/contrib/gcc-changelog/git_repository.py b/contrib/gcc-changelog/git_repository.py
index a419bd97701..4f0d21af039 100755
--- a/contrib/gcc-changelog/git_repository.py
+++ b/contrib/gcc-changelog/git_repository.py
@@ -26,16 +26,38 @@ except ImportError:
     print('  Debian, Ubuntu: python3-git')
     exit(1)
 
-from git_commit import GitCommit
+from git_commit import GitCommit, GitInfo
 
 
 def parse_git_revisions(repo_path, revisions, strict=False):
     repo = Repo(repo_path)
 
-    def commit_to_date(commit):
+    def commit_to_info(commit):
         try:
             c = repo.commit(commit)
-            return datetime.utcfromtimestamp(c.committed_date)
+            diff = repo.commit(commit + '~').diff(commit)
+
+            modified_files = []
+            for file in diff:
+                if file.new_file:
+                    t = 'A'
+                elif file.deleted_file:
+                    t = 'D'
+                elif file.renamed_file:
+                    # Consider that renamed files are two operations:
+                    # the deletion of the original name
+                    # and the addition of the new one.
+                    modified_files.append((file.a_path, 'D'))
+                    t = 'A'
+                else:
+                    t = 'M'
+                modified_files.append((file.b_path, t))
+
+            date = datetime.utcfromtimestamp(c.committed_date)
+            author = '%s  <%s>' % (c.author.name, c.author.email)
+            git_info = GitInfo(c.hexsha, date, author,
+                               c.message.split('\n'), modified_files)
+            return git_info
         except ValueError:
             return None
 
@@ -46,28 +68,7 @@ def parse_git_revisions(repo_path, revisions, strict=False):
         commits = [repo.commit(revisions)]
 
     for commit in commits:
-        diff = repo.commit(commit.hexsha + '~').diff(commit.hexsha)
-
-        modified_files = []
-        for file in diff:
-            if file.new_file:
-                t = 'A'
-            elif file.deleted_file:
-                t = 'D'
-            elif file.renamed_file:
-                # Consider that renamed files are two operations: the deletion
-                # of the original name and the addition of the new one.
-                modified_files.append((file.a_path, 'D'))
-                t = 'A'
-            else:
-                t = 'M'
-            modified_files.append((file.b_path, t))
-
-        date = datetime.utcfromtimestamp(commit.committed_date)
-        author = '%s  <%s>' % (commit.author.name, commit.author.email)
-        git_commit = GitCommit(commit.hexsha, date, author,
-                               commit.message.split('\n'), modified_files,
-                               strict=strict,
-                               commit_to_date_hook=commit_to_date)
+        git_commit = GitCommit(commit_to_info(commit.hexsha), strict=strict,
+                               commit_to_info_hook=commit_to_info)
         parsed_commits.append(git_commit)
     return parsed_commits
-- 
2.27.0

Reply via email to