From 29526b80626016ce7f8142e87f4d9a48cad887ad Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Tue, 4 Jul 2017 16:59:36 +0200 Subject: [PATCH 1/6] fix(cygwin): use `cygpath.exe` to respect different mounts (e.g. MSYS2) + Always process paths, not only when starting from `cygdrive`. --- git/util.py | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/git/util.py b/git/util.py index 5553a0aa9..77121f218 100644 --- a/git/util.py +++ b/git/util.py @@ -221,7 +221,25 @@ def is_exec(fpath): return progs -def _cygexpath(drive, path): +def _cygpath(winpath): + """Invokes `cygpath` cmd to parse Windoews paths.""" + import subprocess as sbp + + cmd = ['cygpath', winpath] + try: + cygpath = sbp.check_output(cmd, universal_newlines=True) + cygpath = cygpath and cygpath[:-1] + except Exception as ex: + log.warning("`cygpath.exe` failed on '%s' due to: %s" + " Using winpath as it is.", + winpath, ex) + else: + winpath = cygpath + + return winpath + + +def _cyg_regex_path(drive, path): if osp.isabs(path) and not drive: ## Invoked from `cygpath()` directly with `D:Apps\123`? # It's an error, leave it alone just slashes) @@ -235,7 +253,7 @@ def _cygexpath(drive, path): else: p = cygpath(p) elif drive: - p = '/cygdrive/%s/%s' % (drive.lower(), p) + return _cygpath('%s:\\%s' % (drive, p)) return p.replace('\\', '/') @@ -249,12 +267,12 @@ def _cygexpath(drive, path): ), (re.compile(r"\\\\\?\\(\w):[/\\](.*)"), - _cygexpath, + _cyg_regex_path, False ), (re.compile(r"(\w):[/\\](.*)"), - _cygexpath, + _cyg_regex_path, False ), @@ -270,16 +288,15 @@ def _cygexpath(drive, path): def cygpath(path): """Use :meth:`git.cmd.Git.polish_url()` instead, that works on any environment.""" - if not path.startswith(('/cygdrive', '//')): - for regex, parser, recurse in _cygpath_parsers: - match = regex.match(path) - if match: - path = parser(*match.groups()) - if recurse: - path = cygpath(path) - break - else: - path = _cygexpath(None, path) + for regex, parser, recurse in _cygpath_parsers: + match = regex.match(path) + if match: + path = parser(*match.groups()) + if recurse: + path = cygpath(path) + break + else: + path = _cyg_regex_path(None, path) return path From a1ce01cadc01c45b4121478b3ef4848aa590542f Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Tue, 4 Jul 2017 17:42:48 +0200 Subject: [PATCH 2/6] feat(cygwin): lru-cache generated paths. --- git/util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git/util.py b/git/util.py index 77121f218..4c0a5a0b3 100644 --- a/git/util.py +++ b/git/util.py @@ -4,7 +4,7 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php import contextlib -from functools import wraps +from functools import wraps, lru_cache import getpass import logging import os @@ -286,6 +286,7 @@ def _cyg_regex_path(drive, path): ) +@lru_cache() def cygpath(path): """Use :meth:`git.cmd.Git.polish_url()` instead, that works on any environment.""" for regex, parser, recurse in _cygpath_parsers: From 38883e055d1adbd9e504bdb3bafd8f2ac5fb7915 Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Tue, 4 Jul 2017 17:43:37 +0200 Subject: [PATCH 3/6] fix(cygwin): `lru_cach` did not exist for py < 3.2 --- git/util.py | 8 ++++++-- requirements.txt | 2 ++ setup.py | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/git/util.py b/git/util.py index 4c0a5a0b3..50df1a8dd 100644 --- a/git/util.py +++ b/git/util.py @@ -4,7 +4,7 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php import contextlib -from functools import wraps, lru_cache +from functools import wraps import getpass import logging import os @@ -18,6 +18,10 @@ from unittest import SkipTest except ImportError: from unittest2 import SkipTest +try: + from functools import lru_cache +except ImportError: + from repoze.lru import lru_cache from gitdb.util import (# NOQA @IgnorePep8 make_sha, @@ -286,7 +290,7 @@ def _cyg_regex_path(drive, path): ) -@lru_cache() +@lru_cache(500) # Sice arg required only for py3.2 backport `repoze.lru` lib. def cygpath(path): """Use :meth:`git.cmd.Git.polish_url()` instead, that works on any environment.""" for regex, parser, recurse in _cygpath_parsers: diff --git a/requirements.txt b/requirements.txt index a8e7a7a8a..f8701062b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ gitdb>=0.6.4 ddt>=1.1.1 +ordereddict; python_version < '2.7' +repoze.lru; python_version < '3.2' unittest2; python_version < '2.7' diff --git a/setup.py b/setup.py index 585a9e29c..f8c36e88d 100755 --- a/setup.py +++ b/setup.py @@ -68,6 +68,7 @@ def _stamp_version(filename): install_requires = ['gitdb2 >= 2.0.0'] extras_require = { ':python_version == "2.6"': ['ordereddict'], + ':python_version < "3.2"': ['repoze.lru'], } test_requires = ['ddt>=1.1.1'] if sys.version_info[:2] < (2, 7): From 1fbcf366f8781ec5533f0fdb9ca4d1852a5f23c4 Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Tue, 4 Jul 2017 20:24:35 +0200 Subject: [PATCH 4/6] fix(cygwin): pass through `cygpath.exe -w` also in reverse --- git/util.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/git/util.py b/git/util.py index 50df1a8dd..bd81f3968 100644 --- a/git/util.py +++ b/git/util.py @@ -225,14 +225,17 @@ def is_exec(fpath): return progs -def _cygpath(winpath): +def _cygpath(winpath, inverse=False): """Invokes `cygpath` cmd to parse Windoews paths.""" import subprocess as sbp cmd = ['cygpath', winpath] + if inverse: + cmd.insert(1, '-w') try: cygpath = sbp.check_output(cmd, universal_newlines=True) - cygpath = cygpath and cygpath[:-1] + if cygpath and cygpath[-1] == '\n': + cygpath = cygpath[:-1] except Exception as ex: log.warning("`cygpath.exe` failed on '%s' due to: %s" " Using winpath as it is.", @@ -290,7 +293,7 @@ def _cyg_regex_path(drive, path): ) -@lru_cache(500) # Sice arg required only for py3.2 backport `repoze.lru` lib. +@lru_cache(500) # Size arg required only for py3.2 backport `repoze.lru` lib. def cygpath(path): """Use :meth:`git.cmd.Git.polish_url()` instead, that works on any environment.""" for regex, parser, recurse in _cygpath_parsers: @@ -309,13 +312,15 @@ def cygpath(path): _decygpath_regex = re.compile(r"/cygdrive/(\w)(/.*)?") +@lru_cache(500) # Size arg required only for py3.2 backport `repoze.lru` lib. def decygpath(path): - m = _decygpath_regex.match(path) - if m: - drive, rest_path = m.groups() - path = '%s:%s' % (drive.upper(), rest_path or '') + if path: + winpath = _cygpath(path, inverse=True) + if path[-1] in '/\\' and winpath[-1] not in '/\\': + winpath += '\\' + path = winpath - return path.replace('/', '\\') + return path #: Store boolean flags denoting if a specific Git executable @@ -323,6 +328,7 @@ def decygpath(path): _is_cygwin_cache = {} +@lru_cache(50) # Size arg required only for py3.2 backport `repoze.lru` lib. def is_cygwin_git(git_executable): if not is_win: return False From fcfdafd860061a486d59f58351a47e6a1890c536 Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Tue, 11 Jul 2017 16:59:39 +0200 Subject: [PATCH 5/6] fix(cygwin): MSYS2 is CYGWIN-derivative, so `is_cygwin_git()` --> true --- git/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/util.py b/git/util.py index bd81f3968..7a9f9fb07 100644 --- a/git/util.py +++ b/git/util.py @@ -350,7 +350,7 @@ def is_cygwin_git(git_executable): universal_newlines=True) uname_out, _ = process.communicate() #retcode = process.poll() - is_cygwin = 'CYGWIN' in uname_out + is_cygwin = 'CYGWIN' in uname_out or 'MSYS' in uname_out except Exception as ex: log.debug('Failed checking if running in CYGWIN due to: %r', ex) _is_cygwin_cache[git_executable] = is_cygwin From 7fd415408a57de5998702fa80a8dd6d823151607 Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Tue, 4 Jul 2017 23:14:52 +0200 Subject: [PATCH 6/6] doc(changes): explain `cygpath.exe` fix --- doc/source/changes.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/doc/source/changes.rst b/doc/source/changes.rst index 4aedf9365..c53c57825 100644 --- a/doc/source/changes.rst +++ b/doc/source/changes.rst @@ -6,6 +6,10 @@ Changelog ==================================== * support for worktrees +* fix(cygwin): use ``cygpath.exe`` to convert *Windows* paths and respect + different mount-points (e.g. *MSYS2* which is a *Cygwin* clone mounts drives + under root). + 2.1.3 - Bugfixes ==================================== @@ -34,7 +38,7 @@ Notable fixes * The `GIT_DIR` environment variable does not override the `path` argument when initializing a `Repo` object anymore. However, if said `path` unset, `GIT_DIR` will be used to fill the void. - + All issues and PRs can be viewed in all detail when following this URL: https://github.com/gitpython-developers/GitPython/issues?q=is%3Aclosed+milestone%3A%22v2.1.0+-+proper+windows+support%22 @@ -63,7 +67,7 @@ https://github.com/gitpython-developers/GitPython/issues?q=is%3Aclosed+milestone 2.0.7 - New Features ==================== -* `IndexFile.commit(...,skip_hooks=False)` added. This parameter emulates the +* `IndexFile.commit(...,skip_hooks=False)` added. This parameter emulates the behaviour of `--no-verify` on the command-line. 2.0.6 - Fixes and Features @@ -103,7 +107,7 @@ https://github.com/gitpython-developers/GitPython/issues?q=is%3Aclosed+milestone commit messages contained ``\r`` characters * Fix: progress handler exceptions are not caught anymore, which would usually just hide bugs previously. -* Fix: The `Git.execute` method will now redirect `stdout` to `devnull` if `with_stdout` is false, +* Fix: The `Git.execute` method will now redirect `stdout` to `devnull` if `with_stdout` is false, which is the intended behaviour based on the parameter's documentation. 2.0.2 - Fixes