| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent e767111 commit e8eae18
6 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,8 +7,9 @@ Changelog | |||
| 7 | 7 | * push/pull/fetch operations will not block anymore | |
| 8 | 8 | * diff() can now properly detect renames, both in patch and raw format. Previously it only worked when create_patch was True. | |
| 9 | 9 | * repo.odb.update_cache() is now called automatically after fetch and pull operations. In case you did that in your own code, you might want to remove your line to prevent a double-update that causes unnecessary IO. | |
| 10 | - * A list of all fixed issues can be found here: https://github.com/gitpython-developers/GitPython/issues?q=milestone%3A%22v0.3.5+-+bugfixes%22+ | ||
| 11 | 10 | * `Repo(path)` will not automatically search upstream anymore and find any git directory on its way up. If you need that behaviour, you can turn it back on using the new `search_parent_directories=True` flag when constructing a `Repo` object. | |
| 11 | + * IndexFile.commit() now runs the `pre-commit` and `post-commit` hooks. Verified to be working on posix systems only. | ||
| 12 | + * A list of all fixed issues can be found here: https://github.com/gitpython-developers/GitPython/issues?q=milestone%3A%22v0.3.5+-+bugfixes%22+ | ||
| 12 | 13 | ||
| 13 | 14 | 0.3.4 - Python 3 Support | |
| 14 | 15 | ======================== | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -502,7 +502,7 @@ def execute(self, command, | |||
| 502 | 502 | # Prevent cmd prompt popups on windows by using a shell ... . | |
| 503 | 503 | # See https://github.com/gitpython-developers/GitPython/pull/126 | |
| 504 | 504 | shell=sys.platform == 'win32', | |
| 505 | - close_fds=(os.name == 'posix'), # unsupported on linux | ||
| 505 | + close_fds=(os.name == 'posix'), # unsupported on windows | ||
| 506 | 506 | **subprocess_kwargs | |
| 507 | 507 | ) | |
| 508 | 508 | if as_process: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -11,17 +11,14 @@ | |||
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | 13 | class InvalidGitRepositoryError(Exception): | |
| 14 | - | ||
| 15 | 14 | """ Thrown if the given repository appears to have an invalid format. """ | |
| 16 | 15 | ||
| 17 | 16 | ||
| 18 | 17 | class NoSuchPathError(OSError): | |
| 19 | - | ||
| 20 | 18 | """ Thrown if a path could not be access by the system. """ | |
| 21 | 19 | ||
| 22 | 20 | ||
| 23 | 21 | class GitCommandError(Exception): | |
| 24 | - | ||
| 25 | 22 | """ Thrown if execution of the git command fails with non-zero status code. """ | |
| 26 | 23 | ||
| 27 | 24 | def __init__(self, command, status, stderr=None, stdout=None): | |
@@ -41,7 +38,6 @@ def __str__(self): | |||
| 41 | 38 | ||
| 42 | 39 | ||
| 43 | 40 | class CheckoutError(Exception): | |
| 44 | - | ||
| 45 | 41 | """Thrown if a file could not be checked out from the index as it contained | |
| 46 | 42 | changes. | |
| 47 | 43 | ||
@@ -71,6 +67,20 @@ class CacheError(Exception): | |||
| 71 | 67 | ||
| 72 | 68 | ||
| 73 | 69 | class UnmergedEntriesError(CacheError): | |
| 74 | - | ||
| 75 | 70 | """Thrown if an operation cannot proceed as there are still unmerged | |
| 76 | 71 | entries in the cache""" | |
| 72 | + | ||
| 73 | + | ||
| 74 | + class HookExecutionError(Exception): | ||
| 75 | + """Thrown if a hook exits with a non-zero exit code. It provides access to the exit code and the string returned | ||
| 76 | + via standard output""" | ||
| 77 | + | ||
| 78 | + def __init__(self, command, status, stdout, stderr): | ||
| 79 | + self.command = command | ||
| 80 | + self.status = status | ||
| 81 | + self.stdout = stdout | ||
| 82 | + self.stderr = stderr | ||
| 83 | + | ||
| 84 | + def __str__(self): | ||
| 85 | + return ("'%s' hook returned with exit code %i\nstdout: '%s'\nstderr: '%s'" | ||
| 86 | + % (self.command, self.status, self.stdout, self.stderr)) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -63,7 +63,8 @@ | |||
| 63 | 63 | aggressive_tree_merge, | |
| 64 | 64 | write_tree_from_cache, | |
| 65 | 65 | stat_mode_to_index_mode, | |
| 66 | - S_IFGITLINK | ||
| 66 | + S_IFGITLINK, | ||
| 67 | + run_commit_hook | ||
| 67 | 68 | ) | |
| 68 | 69 | ||
| 69 | 70 | from gitdb.base import IStream | |
@@ -893,9 +894,12 @@ def commit(self, message, parent_commits=None, head=True, author=None, committer | |||
| 893 | 894 | :note: If you have manually altered the .entries member of this instance, | |
| 894 | 895 | don't forget to write() your changes to disk beforehand. | |
| 895 | 896 | :return: Commit object representing the new commit""" | |
| 897 | + run_commit_hook('pre-commit', self) | ||
| 896 | 898 | tree = self.write_tree() | |
| 897 | - return Commit.create_from_tree(self.repo, tree, message, parent_commits, | ||
| 899 | + rval = Commit.create_from_tree(self.repo, tree, message, parent_commits, | ||
| 898 | 900 | head, author=author, committer=committer) | |
| 901 | + run_commit_hook('post-commit', self) | ||
| 902 | + return rval | ||
| 899 | 903 | ||
| 900 | 904 | @classmethod | |
| 901 | 905 | def _flush_stdin_and_wait(cls, proc, ignore_stdout=False): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,9 +13,14 @@ | |||
| 13 | 13 | S_IFGITLINK = S_IFLNK | S_IFDIR # a submodule | |
| 14 | 14 | ||
| 15 | 15 | from io import BytesIO | |
| 16 | + import os | ||
| 17 | + import subprocess | ||
| 16 | 18 | ||
| 17 | 19 | from git.util import IndexFileSHA1Writer | |
| 18 | - from git.exc import UnmergedEntriesError | ||
| 20 | + from git.exc import ( | ||
| 21 | + UnmergedEntriesError, | ||
| 22 | + HookExecutionError | ||
| 23 | + ) | ||
| 19 | 24 | from git.objects.fun import ( | |
| 20 | 25 | tree_to_stream, | |
| 21 | 26 | traverse_tree_recursive, | |
@@ -37,10 +42,46 @@ | |||
| 37 | 42 | ||
| 38 | 43 | from gitdb.base import IStream | |
| 39 | 44 | from gitdb.typ import str_tree_type | |
| 40 | - from git.compat import defenc | ||
| 45 | + from git.compat import ( | ||
| 46 | + defenc, | ||
| 47 | + force_text | ||
| 48 | + ) | ||
| 41 | 49 | ||
| 42 | 50 | __all__ = ('write_cache', 'read_cache', 'write_tree_from_cache', 'entry_key', | |
| 43 | - 'stat_mode_to_index_mode', 'S_IFGITLINK') | ||
| 51 | + 'stat_mode_to_index_mode', 'S_IFGITLINK', 'run_commit_hook', 'hook_path') | ||
| 52 | + | ||
| 53 | + | ||
| 54 | + def hook_path(name, git_dir): | ||
| 55 | + """:return: path to the given named hook in the given git repository directory""" | ||
| 56 | + return os.path.join(git_dir, 'hooks', name) | ||
| 57 | + | ||
| 58 | + | ||
| 59 | + def run_commit_hook(name, index): | ||
| 60 | + """Run the commit hook of the given name. Silently ignores hooks that do not exist. | ||
| 61 | + :param name: name of hook, like 'pre-commit' | ||
| 62 | + :param index: IndexFile instance | ||
| 63 | + :raises HookExecutionError: """ | ||
| 64 | + hp = hook_path(name, index.repo.git_dir) | ||
| 65 | + if not os.access(hp, os.X_OK): | ||
| 66 | + return | ||
| 67 | + | ||
| 68 | + env = os.environ.copy() | ||
| 69 | + env['GIT_INDEX_FILE'] = index.path | ||
| 70 | + env['GIT_EDITOR'] = ':' | ||
| 71 | + cmd = subprocess.Popen(hp, | ||
| 72 | + env=env, | ||
| 73 | + stdout=subprocess.PIPE, | ||
| 74 | + stderr=subprocess.PIPE, | ||
| 75 | + close_fds=(os.name == 'posix')) | ||
| 76 | + stdout, stderr = cmd.communicate() | ||
| 77 | + cmd.stdout.close() | ||
| 78 | + cmd.stderr.close() | ||
| 79 | + | ||
| 80 | + if cmd.returncode != 0: | ||
| 81 | + stdout = force_text(stdout, defenc) | ||
| 82 | + stderr = force_text(stderr, defenc) | ||
| 83 | + raise HookExecutionError(hp, cmd.returncode, stdout, stderr) | ||
| 84 | + # end handle return code | ||
| 44 | 85 | ||
| 45 | 86 | ||
| 46 | 87 | def stat_mode_to_index_mode(mode): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,6 +12,7 @@ | |||
| 12 | 12 | with_rw_repo | |
| 13 | 13 | ) | |
| 14 | 14 | from git.util import Actor | |
| 15 | + from git.exc import HookExecutionError | ||
| 15 | 16 | from git import ( | |
| 16 | 17 | IndexFile, | |
| 17 | 18 | BlobFilter, | |
@@ -40,6 +41,7 @@ | |||
| 40 | 41 | BaseIndexEntry, | |
| 41 | 42 | IndexEntry | |
| 42 | 43 | ) | |
| 44 | + from git.index.fun import hook_path | ||
| 43 | 45 | ||
| 44 | 46 | ||
| 45 | 47 | class TestIndex(TestBase): | |
@@ -665,6 +667,25 @@ def make_paths(): | |||
| 665 | 667 | assert fkey not in index.entries | |
| 666 | 668 | ||
| 667 | 669 | index.add(files, write=True) | |
| 670 | + if os.name != 'nt': | ||
| 671 | + hp = hook_path('pre-commit', index.repo.git_dir) | ||
| 672 | + with open(hp, "wt") as fp: | ||
| 673 | + fp.write("#!/usr/bin/env sh\necho stdout; echo stderr 1>&2; exit 1") | ||
| 674 | + # end | ||
| 675 | + os.chmod(hp, 0o544) | ||
| 676 | + try: | ||
| 677 | + index.commit("This should fail") | ||
| 678 | + except HookExecutionError as err: | ||
| 679 | + assert err.status == 1 | ||
| 680 | + assert err.command == hp | ||
| 681 | + assert err.stdout == 'stdout\n' | ||
| 682 | + assert err.stderr == 'stderr\n' | ||
| 683 | + assert str(err) | ||
| 684 | + else: | ||
| 685 | + raise AssertionError("Should have cought a HookExecutionError") | ||
| 686 | + # end exception handling | ||
| 687 | + os.remove(hp) | ||
| 688 | + # end hook testing | ||
| 668 | 689 | nc = index.commit("2 files committed", head=False) | |
| 669 | 690 | ||
| 670 | 691 | for fkey in keys: | |
| Back | FazBrowse Home | New Git URL |
0 commit comments