diff --git a/hooks/config.py b/hooks/config.py index aafe0f94..85943472 100644 --- a/hooks/config.py +++ b/hooks/config.py @@ -93,6 +93,7 @@ "hooks.no-precommit-check": {"default": (), "type": tuple}, "hooks.no-rh-character-range-check": {"default": False, "type": bool}, "hooks.no-rh-style-checks": {"default": (), "type": tuple}, + "hooks.no-rh-near-revert-check": {"default": False, "type": bool}, "hooks.no-style-checks": {"default": (), "type": tuple}, "hooks.pre-receive-hook": {"default": None}, "hooks.post-receive-hook": {"default": None}, diff --git a/hooks/pre_commit_checks.py b/hooks/pre_commit_checks.py index 1fc39abd..8d976a9c 100644 --- a/hooks/pre_commit_checks.py +++ b/hooks/pre_commit_checks.py @@ -309,6 +309,29 @@ def check_missing_ticket_number(commit): ) +def reject_almost_reversions(commit): + """Raise InvalidUpdate if the commit's revlog contains "This reverts + commit" in it. + + The GCC ChangeLog scripts expect a line that contains that sentence to also + contain a reference to the commit being reverted. If this line is altered, + therefore, they will fail. + + PARAMETERS + commit: A CommitInfo object corresponding to the commit being checked. + """ + if git_config("hooks.no-rh-near-revert-check"): + return + + if "This reverts commit" in commit.raw_revlog: + raise InvalidUpdate( + "Commit %s looks like it was intended as a revert." % commit.rev, + "", + "When reverting, you should leave the 'This reverts commit'", + "line unaltered.", + ) + + def check_revision_history(commit): """Apply pre-commit checks to the commit's revision history. @@ -321,6 +344,7 @@ def check_revision_history(commit): return # Various checks on the revision history... + reject_almost_reversions(commit) ensure_iso_8859_15_only(commit) ensure_empty_line_after_subject(commit) reject_lines_too_long(commit) diff --git a/hooks/updates/commits.py b/hooks/updates/commits.py index 717cd836..d8c93590 100644 --- a/hooks/updates/commits.py +++ b/hooks/updates/commits.py @@ -4,6 +4,19 @@ from io_utils import safe_decode from updates.mailinglists import expanded_mailing_list from utils import debug +import re + + +_REVERT_COMMIT_RE = re.compile( + r"^This reverts commit (?P[0-9a-f]+)\.$", + re.M +) +"""A regex that matches the same revert lines as ``revert_regex`` does in +``contrib/gcc-changelog/git_commit.py``. + +Note that this regex is matched against an entire body of a commit rather than +each line in it, though. +""" class CommitInfo(object): @@ -235,11 +248,7 @@ def is_revert(self): revision log of such commits, hoping that a user is not deleting them afterwards. """ - if "This reverts commit" in self.raw_revlog: - return True - - # No recognizable pattern. Probably not a revert commit. - return False + return bool(_REVERT_COMMIT_RE.search(self.raw_revlog)); @classmethod def __all_files_from_commit_rev(cls, rev): diff --git a/testsuite/tests/GCC__stricter_revert_pattern/bare_repo_config b/testsuite/tests/GCC__stricter_revert_pattern/bare_repo_config new file mode 100644 index 00000000..e69de29b diff --git a/testsuite/tests/GCC__stricter_revert_pattern/git-repos.tar.bz2 b/testsuite/tests/GCC__stricter_revert_pattern/git-repos.tar.bz2 new file mode 100644 index 00000000..c058cede Binary files /dev/null and b/testsuite/tests/GCC__stricter_revert_pattern/git-repos.tar.bz2 differ diff --git a/testsuite/tests/GCC__stricter_revert_pattern/hooks_config b/testsuite/tests/GCC__stricter_revert_pattern/hooks_config new file mode 100644 index 00000000..11b37fed --- /dev/null +++ b/testsuite/tests/GCC__stricter_revert_pattern/hooks_config @@ -0,0 +1,95 @@ +[hooks] + from-domain = gcc.gnu.org + mailinglist = true + + # We do not want to force a maximum line length in commit + # revision logs, as they get in the way of copy-pasting + # debugging session, error messages, logs, etc. + max-rh-line-length = 0 + + # We allow a 0.5MiB email diff maximum. + max-email-diff-size = 524288 + + # Reject merge commits on a certain number of branches: + # - on master: We request that people rebase their changes + # before pushing instead (merge commits tend to confuse + # git newcomers). + # - on release: We apply the same policy to release branches + # as we have on master. + reject-merge-commits = refs/heads/master,refs/heads/trunk,refs/heads/releases/.* + + # The URL where we can inspect the commit, inserted in the commit + # notification email, and also copy sent to the file-commit-cmd. + commit-url = "https://gcc.gnu.org/g:%(rev)s" + + # This style checker does nothing at present. + # style-checker = /git/gcc.git/hooks-bin/style_checker + + # Send a copy to bugzilla if a commit has a PR number in it. + # The script is a wrapper around + # /sourceware/infra/bin/email-to-bugzilla. + # file-commit-cmd = "/git/gcc.git/hooks-bin/email-to-bugzilla-filtered" + + # Work around + # to allow larger merges. + max-commit-emails = 5000 + + # Allow deliberate merges to use the default commit message. + # Branches that do not allow merge commits are listed in + # reject-merge-commits. + disable-merge-commit-checks = true + + # Do not send emails for commits that are already in the + # repository being added to development branches or user or + # vendor branches (through merges or rebases). + email-new-commits-only = refs/heads/devel/.* + email-new-commits-only = refs/users/.* + email-new-commits-only = refs/vendors/.* + + # GCC-specific ref naming conventions for user and vendor + # branches. + branch-ref-namespace = refs/users/[^/]*/heads/.* + branch-ref-namespace = refs/vendors/[^/]*/heads/.* + + # GCC-specific ref naming conventions for user and vendor + # tags. + tag-ref-namespace = refs/users/[^/]*/tags/.* + tag-ref-namespace = refs/vendors/[^/]*/tags/.* + + # Branch deletion is disabled by default. + restrict-branch-deletion = true + + # Branch deletion is allowed for user and vendor branches. + allow-delete-branch = refs/users/[^/]*/heads/.* + allow-delete-branch = refs/vendors/[^/]*/heads/.* + + # Non-fast-forward updates are allowed in the user and vendor + # namespaces. + allow-non-fast-forward = refs/users/.* + allow-non-fast-forward = refs/vendors/.* + + # Message to give for rejected branch deletion. + rejected-branch-deletion-tip = Branch deletion is only allowed for user and vendor branches. If another branch was created by mistake, contact an administrator to delete it on the server with git update-ref. If a development branch is dead, also contact an administrator to move it under refs/dead/heads/ rather than deleting it. + + # Commit messages should not be restricted to ISO-8859-15. + no-rh-character-range-check = true + + # Custom checker script for each new commit of each ref being + # updated. This makes several checks on the commit message, + # including for ChangeLog formatting and contents. + # commit-extra-checker = /git/gcc.git/hooks-bin/commit_checker + + # Custom checker script for ref updates. This checks for + # branch naming conventions and not introducing new references + # to the git-svn history. + # update-hook = /git/gcc.git/hooks-bin/update_hook + + # Custom email formatter. This inserts GCC monotonically + # increasing commit ids in the commit emails. + # commit-email-formatter = /git/gcc.git/hooks-bin/commit_email_formatter + + # For GCC/Rust development that happens outside of GCC proper, + # , the Git commit messages + # don't always adhere to standard GCC style; see + # . + no-precommit-check = refs/heads/devel/rust/.* diff --git a/testsuite/tests/GCC__stricter_revert_pattern/run_test.py b/testsuite/tests/GCC__stricter_revert_pattern/run_test.py new file mode 100644 index 00000000..cdc8c69a --- /dev/null +++ b/testsuite/tests/GCC__stricter_revert_pattern/run_test.py @@ -0,0 +1,52 @@ +def test_push_bad_revert_commit(testcase): + """Try pushing trunk...""" + p = testcase.run("git push origin trunk".split()) + testcase.assertNotEqual(p.status, 0, p.image) + testcase.assertRunOutputEqual(p, """\ +remote: *** Commit df3a09266b6685060fb1e11268922b491e3e5cd8 looks like it was intended as a revert. +remote: *** +remote: *** When reverting, you should leave the 'This reverts commit' +remote: *** line unaltered. +remote: error: hook declined to update refs/heads/trunk +To ../bare/repo.git/ + ! [remote rejected] trunk -> trunk (hook declined) +error: failed to push some refs to '../bare/repo.git/' +""") + + +def test_push_good_revert_commit(testcase): + """Try pushing trunk...""" + p = testcase.run("git push origin trunk-good:trunk".split()) + testcase.assertEqual(p.status, 0, p.image) + testcase.assertRunOutputEqual(p, """\ +remote: DEBUG: Content-Type: text/plain; charset="utf-8" +remote: MIME-Version: 1.0 +remote: Content-Transfer-Encoding: quoted-printable +remote: From: Test Suite +remote: To: true +remote: Subject: [repo/trunk] Revert "rs6000: Disassemble opaque modes using subregs to allow optimizations" +remote: X-Act-Checkin: repo +remote: X-Git-Author: Surya Kumari Jangala +remote: X-Git-Refname: refs/heads/trunk +remote: X-Git-Oldrev: dc366d741ae38b1dfb105a67176c1de93cf1ed55 +remote: X-Git-Newrev: 12ec343fa812ffa793dd0f41ecb47d2d06109673 +remote: +remote: https://gcc.gnu.org/g:12ec343fa812ffa793dd0f41ecb47d2d06109673 +remote: +remote: commit 12ec343fa812ffa793dd0f41ecb47d2d06109673 +remote: Author: Surya Kumari Jangala +remote: Date: Sat Apr 11 12:19:45 2026 -0500 +remote: +remote: Revert "rs6000: Disassemble opaque modes using subregs to allow optimizations" +remote: +remote: This reverts commit 69a2c243dd2cf9f77150c0eb86dfbc0931876bc1. +remote: +remote: This will resolve the issue reported in PR124804. +remote: +remote: Diff: +remote: --- +remote: +remote: hooks/post-update: line 5: exec: git-update-server-info: not found +To ../bare/repo.git/ + dc366d7..12ec343 trunk-good -> trunk +""")