Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e8eae18

Browse filesBrowse files
committed
IndexFile.commit() now runs pre-commit and post-commit hooks.
However, it does so only on posix. The test-case will run on posix only as well. Please note that in theory, even on windows we will attempt to run hooks, even though I am not sure that this will actually work. Fixes #81
1 parent e767111 commit e8eae18
Copy full SHA for e8eae18

6 files changed

+89-12Lines changed: 89 additions & 12 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎doc/source/changes.rst‎

Copy file name to clipboardExpand all lines: doc/source/changes.rst
+2-1Lines changed: 2 additions & 1 deletion
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ Changelog
77
* push/pull/fetch operations will not block anymore
88
* diff() can now properly detect renames, both in patch and raw format. Previously it only worked when create_patch was True.
99
* 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+
1110
* `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+
1213

1314
0.3.4 - Python 3 Support
1415
========================
Collapse file

‎git/cmd.py‎

Copy file name to clipboardExpand all lines: git/cmd.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ def execute(self, command,
502502
# Prevent cmd prompt popups on windows by using a shell ... .
503503
# See https://github.com/gitpython-developers/GitPython/pull/126
504504
shell=sys.platform == 'win32',
505-
close_fds=(os.name == 'posix'), # unsupported on linux
505+
close_fds=(os.name == 'posix'), # unsupported on windows
506506
**subprocess_kwargs
507507
)
508508
if as_process:
Collapse file

‎git/exc.py‎

Copy file name to clipboardExpand all lines: git/exc.py
+15-5Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,14 @@
1111

1212

1313
class InvalidGitRepositoryError(Exception):
14-
1514
""" Thrown if the given repository appears to have an invalid format. """
1615

1716

1817
class NoSuchPathError(OSError):
19-
2018
""" Thrown if a path could not be access by the system. """
2119

2220

2321
class GitCommandError(Exception):
24-
2522
""" Thrown if execution of the git command fails with non-zero status code. """
2623

2724
def __init__(self, command, status, stderr=None, stdout=None):
@@ -41,7 +38,6 @@ def __str__(self):
4138

4239

4340
class CheckoutError(Exception):
44-
4541
"""Thrown if a file could not be checked out from the index as it contained
4642
changes.
4743
@@ -71,6 +67,20 @@ class CacheError(Exception):
7167

7268

7369
class UnmergedEntriesError(CacheError):
74-
7570
"""Thrown if an operation cannot proceed as there are still unmerged
7671
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))
Collapse file

‎git/index/base.py‎

Copy file name to clipboardExpand all lines: git/index/base.py
+6-2Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@
6363
aggressive_tree_merge,
6464
write_tree_from_cache,
6565
stat_mode_to_index_mode,
66-
S_IFGITLINK
66+
S_IFGITLINK,
67+
run_commit_hook
6768
)
6869

6970
from gitdb.base import IStream
@@ -893,9 +894,12 @@ def commit(self, message, parent_commits=None, head=True, author=None, committer
893894
:note: If you have manually altered the .entries member of this instance,
894895
don't forget to write() your changes to disk beforehand.
895896
:return: Commit object representing the new commit"""
897+
run_commit_hook('pre-commit', self)
896898
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,
898900
head, author=author, committer=committer)
901+
run_commit_hook('post-commit', self)
902+
return rval
899903

900904
@classmethod
901905
def _flush_stdin_and_wait(cls, proc, ignore_stdout=False):
Collapse file

‎git/index/fun.py‎

Copy file name to clipboardExpand all lines: git/index/fun.py
+44-3Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@
1313
S_IFGITLINK = S_IFLNK | S_IFDIR # a submodule
1414

1515
from io import BytesIO
16+
import os
17+
import subprocess
1618

1719
from git.util import IndexFileSHA1Writer
18-
from git.exc import UnmergedEntriesError
20+
from git.exc import (
21+
UnmergedEntriesError,
22+
HookExecutionError
23+
)
1924
from git.objects.fun import (
2025
tree_to_stream,
2126
traverse_tree_recursive,
@@ -37,10 +42,46 @@
3742

3843
from gitdb.base import IStream
3944
from gitdb.typ import str_tree_type
40-
from git.compat import defenc
45+
from git.compat import (
46+
defenc,
47+
force_text
48+
)
4149

4250
__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
4485

4586

4687
def stat_mode_to_index_mode(mode):
Collapse file

‎git/test/test_index.py‎

Copy file name to clipboardExpand all lines: git/test/test_index.py
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
with_rw_repo
1313
)
1414
from git.util import Actor
15+
from git.exc import HookExecutionError
1516
from git import (
1617
IndexFile,
1718
BlobFilter,
@@ -40,6 +41,7 @@
4041
BaseIndexEntry,
4142
IndexEntry
4243
)
44+
from git.index.fun import hook_path
4345

4446

4547
class TestIndex(TestBase):
@@ -665,6 +667,25 @@ def make_paths():
665667
assert fkey not in index.entries
666668

667669
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
668689
nc = index.commit("2 files committed", head=False)
669690

670691
for fkey in keys:

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.