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 diff --git a/git/util.py b/git/util.py index 5553a0aa9..7a9f9fb07 100644 --- a/git/util.py +++ b/git/util.py @@ -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, @@ -221,7 +225,28 @@ def is_exec(fpath): return progs -def _cygexpath(drive, path): +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) + 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.", + 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 +260,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 +274,12 @@ def _cygexpath(drive, path): ), (re.compile(r"\\\\\?\\(\w):[/\\](.*)"), - _cygexpath, + _cyg_regex_path, False ), (re.compile(r"(\w):[/\\](.*)"), - _cygexpath, + _cyg_regex_path, False ), @@ -268,18 +293,18 @@ def _cygexpath(drive, path): ) +@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.""" - 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 @@ -287,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 @@ -301,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 @@ -322,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 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):