summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS3
-rw-r--r--git/cmd.py4
-rw-r--r--git/index/base.py19
-rw-r--r--git/index/fun.py7
-rw-r--r--git/objects/submodule/base.py6
-rw-r--r--git/refs/symbolic.py10
-rw-r--r--git/remote.py16
-rw-r--r--git/repo/base.py26
-rw-r--r--git/test/test_index.py114
-rw-r--r--git/test/test_repo.py23
-rw-r--r--git/test/test_submodule.py12
-rw-r--r--release-verification-key.asc176
12 files changed, 299 insertions, 117 deletions
diff --git a/AUTHORS b/AUTHORS
index 2b67d6cb..6f93b20d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -22,5 +22,8 @@ Contributors are:
-Anson Mansfield <anson.mansfield _at_ gmail.com>
-Ken Odegard <ken.odegard _at_ gmail.com>
-Alexis Horgix Chotard
+-Piotr Babij <piotr.babij _at_ gmail.com>
+-Mikuláš Poul <mikulaspoul _at_ gmail.com>
+-Charles Bouchard-Légaré <cblegare.atl _at_ ntis.ca>
Portions derived from other open source works and are clearly marked.
diff --git a/git/cmd.py b/git/cmd.py
index e0946e47..13c01401 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -780,8 +780,8 @@ class Git(LazyMixin):
if kill_after_timeout:
watchdog.cancel()
if kill_check.isSet():
- stderr_value = 'Timeout: the command "%s" did not complete in %d ' \
- 'secs.' % (" ".join(command), kill_after_timeout)
+ stderr_value = ('Timeout: the command "%s" did not complete in %d '
+ 'secs.' % (" ".join(command), kill_after_timeout)).encode(defenc)
# strip trailing "\n"
if stdout_value.endswith(b"\n"):
stdout_value = stdout_value[:-1]
diff --git a/git/index/base.py b/git/index/base.py
index 4fee2aae..a9e3a3c7 100644
--- a/git/index/base.py
+++ b/git/index/base.py
@@ -948,6 +948,11 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
:return: Commit object representing the new commit"""
if not skip_hooks:
run_commit_hook('pre-commit', self)
+
+ self._write_commit_editmsg(message)
+ run_commit_hook('commit-msg', self, self._commit_editmsg_filepath())
+ message = self._read_commit_editmsg()
+ self._remove_commit_editmsg()
tree = self.write_tree()
rval = Commit.create_from_tree(self.repo, tree, message, parent_commits,
head, author=author, committer=committer,
@@ -955,6 +960,20 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
if not skip_hooks:
run_commit_hook('post-commit', self)
return rval
+
+ def _write_commit_editmsg(self, message):
+ with open(self._commit_editmsg_filepath(), "wb") as commit_editmsg_file:
+ commit_editmsg_file.write(message.encode(defenc))
+
+ def _remove_commit_editmsg(self):
+ os.remove(self._commit_editmsg_filepath())
+
+ def _read_commit_editmsg(self):
+ with open(self._commit_editmsg_filepath(), "rb") as commit_editmsg_file:
+ return commit_editmsg_file.read().decode(defenc)
+
+ def _commit_editmsg_filepath(self):
+ return osp.join(self.repo.common_dir, "COMMIT_EDITMSG")
@classmethod
def _flush_stdin_and_wait(cls, proc, ignore_stdout=False):
diff --git a/git/index/fun.py b/git/index/fun.py
index 7f7518e1..c01a32b8 100644
--- a/git/index/fun.py
+++ b/git/index/fun.py
@@ -62,10 +62,11 @@ def hook_path(name, git_dir):
return osp.join(git_dir, 'hooks', name)
-def run_commit_hook(name, index):
+def run_commit_hook(name, index, *args):
"""Run the commit hook of the given name. Silently ignores hooks that do not exist.
:param name: name of hook, like 'pre-commit'
:param index: IndexFile instance
+ :param args: arguments passed to hook file
:raises HookExecutionError: """
hp = hook_path(name, index.repo.git_dir)
if not os.access(hp, os.X_OK):
@@ -75,7 +76,7 @@ def run_commit_hook(name, index):
env['GIT_INDEX_FILE'] = safe_decode(index.path) if PY3 else safe_encode(index.path)
env['GIT_EDITOR'] = ':'
try:
- cmd = subprocess.Popen(hp,
+ cmd = subprocess.Popen([hp] + list(args),
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
@@ -93,7 +94,7 @@ def run_commit_hook(name, index):
if cmd.returncode != 0:
stdout = force_text(stdout, defenc)
stderr = force_text(stderr, defenc)
- raise HookExecutionError(hp, cmd.returncode, stdout, stderr)
+ raise HookExecutionError(hp, cmd.returncode, stderr, stdout)
# end handle return code
diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py
index a6b4caed..33151217 100644
--- a/git/objects/submodule/base.py
+++ b/git/objects/submodule/base.py
@@ -278,7 +278,7 @@ class Submodule(IndexObject, Iterable, Traversable):
if not path.startswith(working_tree_linux):
raise ValueError("Submodule checkout path '%s' needs to be within the parents repository at '%s'"
% (working_tree_linux, path))
- path = path[len(working_tree_linux) + 1:]
+ path = path[len(working_tree_linux.rstrip('/')) + 1:]
if not path:
raise ValueError("Absolute submodule path '%s' didn't yield a valid relative path" % path)
# end verify converted relative path makes sense
@@ -358,7 +358,9 @@ class Submodule(IndexObject, Iterable, Traversable):
if sm.exists():
# reretrieve submodule from tree
try:
- return repo.head.commit.tree[path]
+ sm = repo.head.commit.tree[path]
+ sm._name = name
+ return sm
except KeyError:
# could only be in index
index = repo.index
diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
index bef6ba3c..8efeafc5 100644
--- a/git/refs/symbolic.py
+++ b/git/refs/symbolic.py
@@ -96,7 +96,15 @@ class SymbolicReference(object):
if not line:
continue
if line.startswith('#'):
- if line.startswith('# pack-refs with:') and not line.endswith('peeled'):
+ # "# pack-refs with: peeled fully-peeled sorted"
+ # the git source code shows "peeled",
+ # "fully-peeled" and "sorted" as the keywords
+ # that can go on this line, as per comments in git file
+ # refs/packed-backend.c
+ # I looked at master on 2017-10-11,
+ # commit 111ef79afe, after tag v2.15.0-rc1
+ # from repo https://github.com/git/git.git
+ if line.startswith('# pack-refs with:') and 'peeled' not in line:
raise TypeError("PackingType of packed-Refs not understood: %r" % line)
# END abort if we do not understand the packing scheme
continue
diff --git a/git/remote.py b/git/remote.py
index 7261be81..35460f5a 100644
--- a/git/remote.py
+++ b/git/remote.py
@@ -536,10 +536,18 @@ class Remote(LazyMixin, Iterable):
# and: http://stackoverflow.com/a/32991784/548792
#
if 'Unknown subcommand: get-url' in str(ex):
- remote_details = self.repo.git.remote("show", self.name)
- for line in remote_details.split('\n'):
- if ' Push URL:' in line:
- yield line.split(': ')[-1]
+ try:
+ remote_details = self.repo.git.remote("show", self.name)
+ for line in remote_details.split('\n'):
+ if ' Push URL:' in line:
+ yield line.split(': ')[-1]
+ except GitCommandError as ex:
+ if any([msg in str(ex) for msg in ['correct access rights','cannot run ssh']]):
+ # If ssh is not setup to access this repository, see issue 694
+ result = Git().execute(['git','config','--get','remote.%s.url' % self.name])
+ yield result
+ else:
+ raise ex
else:
raise ex
diff --git a/git/repo/base.py b/git/repo/base.py
index d3bdc983..990def64 100644
--- a/git/repo/base.py
+++ b/git/repo/base.py
@@ -209,9 +209,17 @@ class Repo(object):
def close(self):
if self.git:
self.git.clear_cache()
- gc.collect()
+ # Tempfiles objects on Windows are holding references to
+ # open files until they are collected by the garbage
+ # collector, thus preventing deletion.
+ # TODO: Find these references and ensure they are closed
+ # and deleted synchronously rather than forcing a gc
+ # collection.
+ if is_win:
+ gc.collect()
gitdb.util.mman.collect()
- gc.collect()
+ if is_win:
+ gc.collect()
def __eq__(self, rhs):
if isinstance(rhs, Repo):
@@ -905,6 +913,10 @@ class Repo(object):
odbt = kwargs.pop('odbt', odb_default_type)
+ # when pathlib.Path or other classbased path is passed
+ if not isinstance(path, str):
+ path = str(path)
+
## A bug win cygwin's Git, when `--bare` or `--separate-git-dir`
# it prepends the cwd or(?) the `url` into the `path, so::
# git clone --bare /cygwin/d/foo.git C:\\Work
@@ -918,9 +930,9 @@ class Repo(object):
if sep_dir:
kwargs['separate_git_dir'] = Git.polish_url(sep_dir)
proc = git.clone(Git.polish_url(url), clone_path, with_extended_output=True, as_process=True,
- v=True, **add_progress(kwargs, git, progress))
+ v=True, universal_newlines=True, **add_progress(kwargs, git, progress))
if progress:
- handle_process_output(proc, None, progress.new_message_handler(), finalize_process)
+ handle_process_output(proc, None, progress.new_message_handler(), finalize_process, decode_streams=False)
else:
(stdout, stderr) = proc.communicate()
log.debug("Cmd(%s)'s unused stdout: %s", getattr(proc, 'args', ''), stdout)
@@ -931,12 +943,16 @@ class Repo(object):
if not osp.isabs(path) and git.working_dir:
path = osp.join(git._working_dir, path)
+ repo = cls(path, odbt=odbt)
+
+ # retain env values that were passed to _clone()
+ repo.git.update_environment(**git.environment())
+
# adjust remotes - there may be operating systems which use backslashes,
# These might be given as initial paths, but when handling the config file
# that contains the remote from which we were clones, git stops liking it
# as it will escape the backslashes. Hence we undo the escaping just to be
# sure
- repo = cls(path, odbt=odbt)
if repo.remotes:
with repo.remotes[0].config_writer as writer:
writer.set_value('url', Git.polish_url(repo.remotes[0].url))
diff --git a/git/test/test_index.py b/git/test/test_index.py
index e8d38a09..cf746140 100644
--- a/git/test/test_index.py
+++ b/git/test/test_index.py
@@ -729,35 +729,6 @@ class TestIndex(TestBase):
assert fkey not in index.entries
index.add(files, write=True)
- if is_win:
- hp = hook_path('pre-commit', index.repo.git_dir)
- hpd = osp.dirname(hp)
- if not osp.isdir(hpd):
- os.mkdir(hpd)
- with open(hp, "wt") as fp:
- fp.write("#!/usr/bin/env sh\necho stdout; echo stderr 1>&2; exit 1")
- # end
- os.chmod(hp, 0o744)
- try:
- index.commit("This should fail")
- except HookExecutionError as err:
- if is_win:
- self.assertIsInstance(err.status, OSError)
- self.assertEqual(err.command, [hp])
- self.assertEqual(err.stdout, '')
- self.assertEqual(err.stderr, '')
- assert str(err)
- else:
- self.assertEqual(err.status, 1)
- self.assertEqual(err.command, hp)
- self.assertEqual(err.stdout, 'stdout\n')
- self.assertEqual(err.stderr, 'stderr\n')
- assert str(err)
- else:
- raise AssertionError("Should have cought a HookExecutionError")
- # end exception handling
- os.remove(hp)
- # end hook testing
nc = index.commit("2 files committed", head=False)
for fkey in keys:
@@ -859,3 +830,88 @@ class TestIndex(TestBase):
r = Repo.init(rw_dir)
r.index.add([fp])
r.index.commit('Added [.exe')
+
+ @with_rw_repo('HEAD', bare=True)
+ def test_pre_commit_hook_success(self, rw_repo):
+ index = rw_repo.index
+ hp = hook_path('pre-commit', index.repo.git_dir)
+ hpd = osp.dirname(hp)
+ if not osp.isdir(hpd):
+ os.mkdir(hpd)
+ with open(hp, "wt") as fp:
+ fp.write("#!/usr/bin/env sh\nexit 0")
+ os.chmod(hp, 0o744)
+ index.commit("This should not fail")
+
+ @with_rw_repo('HEAD', bare=True)
+ def test_pre_commit_hook_fail(self, rw_repo):
+ index = rw_repo.index
+ hp = hook_path('pre-commit', index.repo.git_dir)
+ hpd = osp.dirname(hp)
+ if not osp.isdir(hpd):
+ os.mkdir(hpd)
+ with open(hp, "wt") as fp:
+ fp.write("#!/usr/bin/env sh\necho stdout; echo stderr 1>&2; exit 1")
+ os.chmod(hp, 0o744)
+ try:
+ index.commit("This should fail")
+ except HookExecutionError as err:
+ if is_win:
+ self.assertIsInstance(err.status, OSError)
+ self.assertEqual(err.command, [hp])
+ self.assertEqual(err.stdout, '')
+ self.assertEqual(err.stderr, '')
+ assert str(err)
+ else:
+ self.assertEqual(err.status, 1)
+ self.assertEqual(err.command, [hp])
+ self.assertEqual(err.stdout, "\n stdout: 'stdout\n'")
+ self.assertEqual(err.stderr, "\n stderr: 'stderr\n'")
+ assert str(err)
+ else:
+ raise AssertionError("Should have cought a HookExecutionError")
+
+ @with_rw_repo('HEAD', bare=True)
+ def test_commit_msg_hook_success(self, rw_repo):
+ index = rw_repo.index
+ commit_message = u"commit default head by Frèderic Çaufl€"
+ from_hook_message = u"from commit-msg"
+
+ hp = hook_path('commit-msg', index.repo.git_dir)
+ hpd = osp.dirname(hp)
+ if not osp.isdir(hpd):
+ os.mkdir(hpd)
+ with open(hp, "wt") as fp:
+ fp.write('#!/usr/bin/env sh\necho -n " {}" >> "$1"'.format(from_hook_message))
+ os.chmod(hp, 0o744)
+
+ new_commit = index.commit(commit_message)
+ self.assertEqual(new_commit.message, u"{} {}".format(commit_message, from_hook_message))
+
+ @with_rw_repo('HEAD', bare=True)
+ def test_commit_msg_hook_fail(self, rw_repo):
+ index = rw_repo.index
+ hp = hook_path('commit-msg', index.repo.git_dir)
+ hpd = osp.dirname(hp)
+ if not osp.isdir(hpd):
+ os.mkdir(hpd)
+ with open(hp, "wt") as fp:
+ fp.write("#!/usr/bin/env sh\necho stdout; echo stderr 1>&2; exit 1")
+ os.chmod(hp, 0o744)
+ try:
+ index.commit("This should fail")
+ except HookExecutionError as err:
+ if is_win:
+ self.assertIsInstance(err.status, OSError)
+ self.assertEqual(err.command, [hp])
+ self.assertEqual(err.stdout, '')
+ self.assertEqual(err.stderr, '')
+ assert str(err)
+ else:
+ self.assertEqual(err.status, 1)
+ self.assertEqual(err.command, [hp])
+ self.assertEqual(err.stdout, "\n stdout: 'stdout\n'")
+ self.assertEqual(err.stderr, "\n stderr: 'stderr\n'")
+ assert str(err)
+ else:
+ raise AssertionError("Should have cought a HookExecutionError")
diff --git a/git/test/test_repo.py b/git/test/test_repo.py
index 312e67f9..2c3ad957 100644
--- a/git/test/test_repo.py
+++ b/git/test/test_repo.py
@@ -16,6 +16,11 @@ try:
except ImportError:
from unittest2 import skipIf, SkipTest
+try:
+ import pathlib
+except ImportError:
+ pathlib = None
+
from git import (
InvalidGitRepositoryError,
Repo,
@@ -201,6 +206,24 @@ class TestRepo(TestBase):
pass
# END test repos with working tree
+ @with_rw_directory
+ def test_clone_from_keeps_env(self, rw_dir):
+ original_repo = Repo.init(osp.join(rw_dir, "repo"))
+ environment = {"entry1": "value", "another_entry": "10"}
+
+ cloned = Repo.clone_from(original_repo.git_dir, osp.join(rw_dir, "clone"), env=environment)
+
+ assert_equal(environment, cloned.git.environment())
+
+ @with_rw_directory
+ def test_clone_from_pathlib(self, rw_dir):
+ if pathlib is None: # pythons bellow 3.4 don't have pathlib
+ raise SkipTest("pathlib was introduced in 3.4")
+
+ original_repo = Repo.init(osp.join(rw_dir, "repo"))
+
+ Repo.clone_from(original_repo.git_dir, pathlib.Path(rw_dir) / "clone_pathlib")
+
def test_init(self):
prev_cwd = os.getcwd()
os.chdir(tempfile.gettempdir())
diff --git a/git/test/test_submodule.py b/git/test/test_submodule.py
index e667ae17..f970dd2c 100644
--- a/git/test/test_submodule.py
+++ b/git/test/test_submodule.py
@@ -10,7 +10,7 @@ except ImportError:
import git
from git.cmd import Git
-from git.compat import string_types
+from git.compat import string_types, is_win
from git.exc import (
InvalidGitRepositoryError,
RepositoryDirtyError
@@ -911,3 +911,13 @@ class TestSubmodule(TestBase):
parent_repo.submodule_update(to_latest_revision=True, force_reset=True)
assert sm_mod.commit() == sm_pfb.commit, "Now head should have been reset"
assert sm_mod.head.ref.name == sm_pfb.name
+
+ @skipIf(not is_win, "Specifically for Windows.")
+ def test_to_relative_path_with_super_at_root_drive(self):
+ class Repo(object):
+ working_tree_dir = 'D:\\'
+ super_repo = Repo()
+ submodule_path = 'D:\\submodule_path'
+ relative_path = Submodule._to_relative_path(super_repo, submodule_path)
+ msg = '_to_relative_path should be "submodule_path" but was "%s"' % relative_path
+ assert relative_path == 'submodule_path', msg \ No newline at end of file
diff --git a/release-verification-key.asc b/release-verification-key.asc
index 53b38913..895ce04f 100644
--- a/release-verification-key.asc
+++ b/release-verification-key.asc
@@ -1,74 +1,110 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: GPGTools - https://gpgtools.org
-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
-=H2BO
+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
+=XcVX
-----END PGP PUBLIC KEY BLOCK-----