diff --git a/git/cmd.py b/git/cmd.py index db308941c..47976df47 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -180,14 +180,18 @@ def __setstate__(self, d): # Override this value using `Git.USE_SHELL = True` USE_SHELL = False - class AutoInterrupt(object): + @classmethod + def polish_url(cls, url): + return url.replace("\\\\", "\\").replace("\\", "/") + class AutoInterrupt(object): """Kill/Interrupt the stored process instance once this instance goes out of scope. It is used to prevent processes piling up in case iterators stop reading. Besides all attributes are wired through to the contained process object. The wait method was overridden to perform automatic status code checking and possibly raise.""" + __slots__ = ("proc", "args") def __init__(self, proc, args): diff --git a/git/remote.py b/git/remote.py index d35e1fad1..a5446ddf4 100644 --- a/git/remote.py +++ b/git/remote.py @@ -29,7 +29,7 @@ join_path, finalize_process ) -from git.cmd import handle_process_output +from git.cmd import handle_process_output, Git from gitdb.util import join from git.compat import (defenc, force_text, is_win) import logging @@ -557,7 +557,7 @@ def create(cls, repo, name, url, **kwargs): :raise GitCommandError: in case an origin with that name already exists""" scmd = 'add' kwargs['insert_kwargs_after'] = scmd - repo.git.remote(scmd, name, url, **kwargs) + repo.git.remote(scmd, name, Git.polish_url(url), **kwargs) return cls(repo, name) # add is an alias diff --git a/git/repo/base.py b/git/repo/base.py index 4e9549eea..c5cdce7c6 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -897,7 +897,7 @@ def _clone(cls, git, url, path, odb_default_type, progress, **kwargs): repo = cls(path, odbt=odbt) if repo.remotes: with repo.remotes[0].config_writer as writer: - writer.set_value('url', repo.remotes[0].url.replace("\\\\", "\\").replace("\\", "/")) + writer.set_value('url', Git.polish_url(repo.remotes[0].url)) # END handle remote repo return repo diff --git a/git/test/lib/helper.py b/git/test/lib/helper.py index e92ce8b40..91cb990f7 100644 --- a/git/test/lib/helper.py +++ b/git/test/lib/helper.py @@ -5,22 +5,24 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php from __future__ import print_function -import os -from unittest import TestCase -import time -import tempfile +from functools import wraps import io import logging +import os +import tempfile +import textwrap +import time +from unittest import TestCase -from functools import wraps - -from git.util import rmtree from git.compat import string_types, is_win -import textwrap +from git.util import rmtree + +import os.path as osp -osp = os.path.dirname -GIT_REPO = os.environ.get("GIT_PYTHON_TEST_GIT_REPO_BASE", osp(osp(osp(osp(__file__))))) +ospd = osp.dirname + +GIT_REPO = os.environ.get("GIT_PYTHON_TEST_GIT_REPO_BASE", ospd(ospd(ospd(ospd(__file__))))) GIT_DAEMON_PORT = os.environ.get("GIT_PYTHON_TEST_GIT_DAEMON_PORT", "9418") __all__ = ( @@ -40,8 +42,8 @@ def fixture_path(name): - test_dir = osp(osp(__file__)) - return os.path.join(test_dir, "fixtures", name) + test_dir = ospd(ospd(__file__)) + return osp.join(test_dir, "fixtures", name) def fixture(name): @@ -50,7 +52,7 @@ def fixture(name): def absolute_project_path(): - return os.path.abspath(os.path.join(osp(__file__), "..", "..")) + return osp.abspath(osp.join(osp(__file__), "..", "..")) #} END routines @@ -170,26 +172,31 @@ def repo_creator(self): return argument_passer -def launch_git_daemon(temp_dir, ip, port): +def launch_git_daemon(base_path, ip, port): from git import Git if is_win: ## On MINGW-git, daemon exists in .\Git\mingw64\libexec\git-core\, # but if invoked as 'git daemon', it detaches from parent `git` cmd, # and then CANNOT DIE! # So, invoke it as a single command. - ## Cygwin-git has no daemon. + ## Cygwin-git has no daemon. But it can use MINGW's. # - daemon_cmd = ['git-daemon', temp_dir, + daemon_cmd = ['git-daemon', '--enable=receive-pack', '--listen=%s' % ip, - '--port=%s' % port] + '--port=%s' % port, + '--base-path=%s' % base_path, + base_path] gd = Git().execute(daemon_cmd, as_process=True) else: - gd = Git().daemon(temp_dir, + gd = Git().daemon(base_path, enable='receive-pack', listen=ip, port=port, + base_path=base_path, as_process=True) + # yes, I know ... fortunately, this is always going to work if sleep time is just large enough + time.sleep(0.5) return gd @@ -217,7 +224,8 @@ def case(self, rw_repo, rw_remote_repo) See working dir info in with_rw_repo :note: We attempt to launch our own invocation of git-daemon, which will be shutdown at the end of the test. """ - from git import Remote, GitCommandError + from git import Git, Remote, GitCommandError + assert isinstance(working_tree_ref, string_types), "Decorator requires ref name for working tree checkout" def argument_passer(func): @@ -245,23 +253,36 @@ def remote_repo_creator(self): pass crw.set(section, "receivepack", True) - # initialize the remote - first do it as local remote and pull, then - # we change the url to point to the daemon. The daemon should be started - # by the user, not by us + # Initialize the remote - first do it as local remote and pull, then + # we change the url to point to the daemon. d_remote = Remote.create(rw_repo, "daemon_origin", remote_repo_dir) d_remote.fetch() - remote_repo_url = "git://localhost:%s%s" % (GIT_DAEMON_PORT, remote_repo_dir) + base_path, rel_repo_dir = osp.split(remote_repo_dir) + + remote_repo_url = "git://localhost:%s/%s" % (GIT_DAEMON_PORT, rel_repo_dir) with d_remote.config_writer as cw: cw.set('url', remote_repo_url) - temp_dir = osp(_mktemp()) - gd = launch_git_daemon(temp_dir, '127.0.0.1', GIT_DAEMON_PORT) try: - # yes, I know ... fortunately, this is always going to work if sleep time is just large enough - time.sleep(0.5) - # end - + gd = launch_git_daemon(Git.polish_url(base_path), '127.0.0.1', GIT_DAEMON_PORT) + except Exception as ex: + if is_win: + msg = textwrap.dedent(""" + The `git-daemon.exe` must be in PATH. + For MINGW, look into .\Git\mingw64\libexec\git-core\), but problems with paths might appear. + CYGWIN has no daemon, but if one exists, it gets along fine (has also paths problems) + Anyhow, alternatively try starting `git-daemon` manually:""") + else: + msg = "Please try starting `git-daemon` manually:" + msg += textwrap.dedent(""" + git daemon --enable=receive-pack --base-path=%s %s + You can also run the daemon on a different port by passing --port=" + and setting the environment variable GIT_PYTHON_TEST_GIT_DAEMON_PORT to + """ % (base_path, base_path)) + raise AssertionError(ex, msg) + # END make assertion + else: # try to list remotes to diagnoes whether the server is up try: rw_repo.git.ls_remote(d_remote) @@ -283,15 +304,6 @@ def remote_repo_creator(self): Anyhow, alternatively try starting `git-daemon` manually:""") else: msg = "Please try starting `git-daemon` manually:" - - msg += textwrap.dedent(""" - git daemon --enable=receive-pack '%s' - You can also run the daemon on a different port by passing --port=" - and setting the environment variable GIT_PYTHON_TEST_GIT_DAEMON_PORT to - """ % temp_dir) - from nose import SkipTest - raise SkipTest(msg) if is_win else AssertionError(msg) - # END make assertion # END catch ls remote error # adjust working dir @@ -359,7 +371,7 @@ class TestBase(TestCase): def _small_repo_url(self): """:return" a path to a small, clonable repository""" - return os.path.join(self.rorepo.working_tree_dir, 'git/ext/gitdb/gitdb/ext/smmap') + return osp.join(self.rorepo.working_tree_dir, 'git/ext/gitdb/gitdb/ext/smmap') @classmethod def setUpClass(cls): @@ -383,7 +395,7 @@ def _make_file(self, rela_path, data, repo=None): with the given data. Returns absolute path to created file. """ repo = repo or self.rorepo - abs_path = os.path.join(repo.working_tree_dir, rela_path) + abs_path = osp.join(repo.working_tree_dir, rela_path) with open(abs_path, "w") as fp: fp.write(data) return abs_path diff --git a/git/test/test_base.py b/git/test/test_base.py index 50f06f054..e5e8f173b 100644 --- a/git/test/test_base.py +++ b/git/test/test_base.py @@ -110,17 +110,6 @@ def test_with_rw_repo(self, rw_repo): assert not rw_repo.config_reader("repository").getboolean("core", "bare") assert os.path.isdir(os.path.join(rw_repo.working_tree_dir, 'lib')) - # @skipIf(HIDE_WINDOWS_KNOWN_ERRORS, """ - # FIXME: helper.wrapper fails with: - # PermissionError: [WinError 5] Access is denied: - # 'C:\\Users\\appveyor\\AppData\\Local\\Temp\\1\\test_work_tree_unsupportedryfa60di\\ - # master_repo\\.git\\objects\\pack\\pack-bc9e0787aef9f69e1591ef38ea0a6f566ec66fe3.idx' - # AND - # FIXME: git-daemon failing with: - # git.exc.GitCommandError: Cmd('git') failed due to: exit code(128) - # cmdline: git ls-remote daemon_origin - # stderr: 'fatal: bad config line 15 in file .git/config' - # """) @with_rw_and_rw_remote_repo('0.1.6') def test_with_rw_remote_and_rw_repo(self, rw_repo, rw_remote_repo): assert not rw_repo.config_reader("repository").getboolean("core", "bare") diff --git a/git/test/test_remote.py b/git/test/test_remote.py index e0b00e0c5..46e290fed 100644 --- a/git/test/test_remote.py +++ b/git/test/test_remote.py @@ -30,6 +30,8 @@ import tempfile import os import random +from unittest.case import skipIf +from git.test.lib.helper import HIDE_WINDOWS_KNOWN_ERRORS # assure we have repeatable results random.seed(0) @@ -384,12 +386,12 @@ def _assert_push_and_pull(self, remote, rw_repo, remote_repo): TagReference.delete(rw_repo, new_tag, other_tag) remote.push(":%s" % other_tag.path) - # @skipIf(HIDE_WINDOWS_KNOWN_ERRORS, """ - # FIXME: git-daemon failing with: - # git.exc.GitCommandError: Cmd('git') failed due to: exit code(128) - # cmdline: git ls-remote daemon_origin - # stderr: 'fatal: bad config line 15 in file .git/config' - # """) + @skipIf(HIDE_WINDOWS_KNOWN_ERRORS, """ + FIXME: git-daemon failing with: + git.exc.GitCommandError: Cmd('git') failed due to: exit code(128) + cmdline: git ls-remote daemon_origin + stderr: 'fatal: bad config line 15 in file .git/config' + """) @with_rw_and_rw_remote_repo('0.1.6') def test_base(self, rw_repo, remote_repo): num_remotes = 0 diff --git a/git/test/test_repo.py b/git/test/test_repo.py index 1d537e931..91b3aac0a 100644 --- a/git/test/test_repo.py +++ b/git/test/test_repo.py @@ -472,12 +472,17 @@ def test_creation_deletion(self): head = self.rorepo.create_head("new_head", "HEAD~1") self.rorepo.delete_head(head) - tag = self.rorepo.create_tag("new_tag", "HEAD~2") - self.rorepo.delete_tag(tag) + try: + tag = self.rorepo.create_tag("new_tag", "HEAD~2") + finally: + self.rorepo.delete_tag(tag) with self.rorepo.config_writer(): pass - remote = self.rorepo.create_remote("new_remote", "git@server:repo.git") - self.rorepo.delete_remote(remote) + try: + remote = self.rorepo.create_remote("new_remote", "git@server:repo.git") + finally: + self.rorepo.delete_remote(remote) + def test_comparison_and_hash(self): # this is only a preliminary test, more testing done in test_index