# HG changeset patch # User Jelmer Vernooij # Date 1607691092 0 # Fri Dec 11 12:51:32 2020 +0000 # Node ID e264112833cc07379d1ff8725f44c364d305ab88 # Parent f8b3b5c1951741ee01acf9c8757eecfc250db6f8 # Parent 5ceae96b68008adb971ceb3dd6d8b7dc573ba875 Merge branch 'commit-no-verify' of git://github.com/pmrowla/dulwich diff --git a/dulwich/porcelain.py b/dulwich/porcelain.py --- a/dulwich/porcelain.py +++ b/dulwich/porcelain.py @@ -323,7 +323,8 @@ repo_obj.refs.set_symbolic_ref(b'HEAD', ref_path) -def commit(repo=".", message=None, author=None, committer=None, encoding=None): +def commit(repo=".", message=None, author=None, committer=None, encoding=None, + no_verify=False): """Create a new commit. Args: @@ -331,6 +332,7 @@ message: Optional commit message author: Optional author name and email committer: Optional committer name and email + no_verify: Skip pre-commit and commit-msg hooks Returns: SHA1 of the new commit """ # FIXME: Support --all argument @@ -344,7 +346,7 @@ with open_repo_closing(repo) as r: return r.do_commit( message=message, author=author, committer=committer, - encoding=encoding) + encoding=encoding, no_verify=no_verify) def commit_tree(repo, tree, message=None, author=None, committer=None): diff --git a/dulwich/repo.py b/dulwich/repo.py --- a/dulwich/repo.py +++ b/dulwich/repo.py @@ -822,7 +822,7 @@ author=None, commit_timestamp=None, commit_timezone=None, author_timestamp=None, author_timezone=None, tree=None, encoding=None, - ref=b'HEAD', merge_heads=None): + ref=b'HEAD', merge_heads=None, no_verify=False): """Create a new commit. If not specified, `committer` and `author` default to @@ -844,6 +844,7 @@ encoding: Encoding ref: Optional ref to commit to (defaults to current branch) merge_heads: Merge heads (defaults to .git/MERGE_HEADS) + no_verify: Skip pre-commit and commit-msg hooks Returns: New commit SHA1 @@ -859,7 +860,8 @@ c.tree = tree try: - self.hooks['pre-commit'].execute() + if not no_verify: + self.hooks['pre-commit'].execute() except HookError as e: raise CommitError(e) except KeyError: # no hook defined, silent fallthrough @@ -903,9 +905,12 @@ raise ValueError("No commit message specified") try: - c.message = self.hooks['commit-msg'].execute(message) - if c.message is None: + if no_verify: c.message = message + else: + c.message = self.hooks['commit-msg'].execute(message) + if c.message is None: + c.message = message except HookError as e: raise CommitError(e) except KeyError: # no hook defined, message not modified diff --git a/dulwich/tests/test_porcelain.py b/dulwich/tests/test_porcelain.py --- a/dulwich/tests/test_porcelain.py +++ b/dulwich/tests/test_porcelain.py @@ -24,12 +24,14 @@ import os import re import shutil +import stat import tarfile import tempfile import time from dulwich import porcelain from dulwich.diff_tree import tree_changes +from dulwich.errors import CommitError from dulwich.objects import ( Blob, Tag, @@ -125,6 +127,52 @@ self.assertTrue(isinstance(sha, bytes)) self.assertEqual(len(sha), 40) + def test_no_verify(self): + if os.name != 'posix': + self.skipTest('shell hook tests requires POSIX shell') + self.assertTrue(os.path.exists('/bin/sh')) + + hooks_dir = os.path.join(self.repo.controldir(), "hooks") + os.makedirs(hooks_dir, exist_ok=True) + self.addCleanup(shutil.rmtree, hooks_dir) + + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) + + hook_fail = "#!/bin/sh\nexit 1" + + # hooks are executed in pre-commit, commit-msg order + # test commit-msg failure first, then pre-commit failure, then + # no_verify to skip both hooks + commit_msg = os.path.join(hooks_dir, "commit-msg") + with open(commit_msg, 'w') as f: + f.write(hook_fail) + os.chmod(commit_msg, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) + + with self.assertRaises(CommitError): + porcelain.commit( + self.repo.path, message="Some message", + author="Joe ", + committer="Bob ") + + pre_commit = os.path.join(hooks_dir, "pre-commit") + with open(pre_commit, 'w') as f: + f.write(hook_fail) + os.chmod(pre_commit, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) + + with self.assertRaises(CommitError): + porcelain.commit( + self.repo.path, message="Some message", + author="Joe ", + committer="Bob ") + + sha = porcelain.commit( + self.repo.path, message="Some message", + author="Joe ", + committer="Bob ", no_verify=True) + self.assertTrue(isinstance(sha, bytes)) + self.assertEqual(len(sha), 40) + class CleanTests(PorcelainTestCase):