summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Thiel <byronimo@gmail.com>2016-10-22 11:11:25 +0200
committerSebastian Thiel <byronimo@gmail.com>2016-10-22 11:11:25 +0200
commitcaa0ea7a0893fe90ea043843d4e6ad407126d7b8 (patch)
tree2f688eb182f2e76091134c47c4a327681c12e15b
parentafcd64ebbb770908bd2a751279ff070dea5bb97c (diff)
parentcc77e6b2862733a211c55cf29cc7a83c36c27919 (diff)
downloadgitpython-caa0ea7a0893fe90ea043843d4e6ad407126d7b8.tar.gz
Merge branch 'cygwin' of https://github.com/ankostis/GitPython into ankostis-cygwin
-rw-r--r--.appveyor.yml43
-rw-r--r--.travis.yml7
-rw-r--r--git/__init__.py7
-rw-r--r--git/cmd.py23
-rw-r--r--git/config.py36
-rw-r--r--git/db.py5
-rw-r--r--git/diff.py13
m---------git/ext/gitdb0
-rw-r--r--git/index/base.py76
-rw-r--r--git/index/fun.py34
-rw-r--r--git/index/util.py12
-rw-r--r--git/objects/base.py13
-rw-r--r--git/objects/commit.py2
-rw-r--r--git/objects/submodule/base.py109
-rw-r--r--git/objects/tag.py9
-rw-r--r--git/objects/tree.py4
-rw-r--r--git/refs/log.py36
-rw-r--r--git/refs/remote.py9
-rw-r--r--git/refs/symbolic.py52
-rw-r--r--git/remote.py35
-rw-r--r--git/repo/base.py143
-rw-r--r--git/repo/fun.py41
-rw-r--r--git/test/lib/helper.py222
-rw-r--r--git/test/performance/lib.py17
-rw-r--r--git/test/performance/test_streams.py23
-rw-r--r--git/test/test_base.py28
-rw-r--r--git/test/test_commit.py42
-rw-r--r--git/test/test_config.py13
-rw-r--r--git/test/test_db.py9
-rw-r--r--git/test/test_diff.py25
-rw-r--r--git/test/test_docs.py26
-rw-r--r--git/test/test_fun.py33
-rw-r--r--git/test/test_git.py28
-rw-r--r--git/test/test_index.py62
-rw-r--r--git/test/test_reflog.py21
-rw-r--r--git/test/test_refs.py28
-rw-r--r--git/test/test_repo.py42
-rw-r--r--git/test/test_submodule.py34
-rw-r--r--git/test/test_tree.py9
-rw-r--r--git/test/test_util.py90
-rw-r--r--git/util.py177
-rw-r--r--requirements.txt3
-rwxr-xr-xsetup.py2
-rw-r--r--test-requirements.txt2
44 files changed, 960 insertions, 685 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 0e9a9473..701fc4ac 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -21,18 +21,17 @@ environment:
IS_CONDA: "yes"
GIT_PATH: "%GIT_DAEMON_PATH%"
- # ## Cygwin
- # #
- # - PYTHON: "C:\\Miniconda-x64"
- # PYTHON_VERSION: "2.7"
- # IS_CONDA: "yes"
- # GIT_PATH: "%CYGWIN_GIT_PATH%"
- # - PYTHON: "C:\\Python34-x64"
- # PYTHON_VERSION: "3.4"
- # GIT_PATH: "%CYGWIN_GIT_PATH%"
- # - PYTHON: "C:\\Python35-x64"
- # PYTHON_VERSION: "3.5"
- # GIT_PATH: "%CYGWIN64_GIT_PATH%"
+ ## Cygwin
+ #
+ - PYTHON: "C:\\Miniconda-x64"
+ PYTHON_VERSION: "2.7"
+ IS_CONDA: "yes"
+ IS_CYGWIN: "yes"
+ GIT_PATH: "%CYGWIN_GIT_PATH%"
+ - PYTHON: "C:\\Python35-x64"
+ PYTHON_VERSION: "3.5"
+ GIT_PATH: "%CYGWIN64_GIT_PATH%"
+ IS_CYGWIN: "yes"
install:
@@ -48,14 +47,12 @@ install:
python --version
python -c "import struct; print(struct.calcsize('P') * 8)"
- - IF "%IS_CONDA%"=="yes" (
+ - IF "%IS_CONDA%" == "yes" (
conda info -a &
conda install --yes --quiet pip
)
- - pip install nose ddt wheel codecov
- - IF "%PYTHON_VERSION%"=="2.7" (
- pip install mock
- )
+ - pip install -r test-requirements.txt
+ - pip install codecov
## Copied from `init-tests-after-clone.sh`.
#
@@ -79,7 +76,15 @@ install:
build: false
test_script:
- - IF "%PYTHON_VERSION%"=="3.5" (nosetests -v --with-coverage) ELSE (nosetests -v)
+ - IF "%IS_CYGWIN%" == "yes" (
+ nosetests -v
+ ) ELSE (
+ IF "%PYTHON_VERSION%" == "3.5" (
+ nosetests -v --with-coverage
+ ) ELSE (
+ nosetests -v
+ )
+ )
on_success:
- - IF "%PYTHON_VERSION%"=="3.5" (codecov)
+ - IF "%PYTHON_VERSION%" == "3.5" IF NOT "%IS_CYGWIN%" == "yes" (codecov)
diff --git a/.travis.yml b/.travis.yml
index 4c191db2..f7dd247b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,14 +1,10 @@
language: python
python:
- - "2.6"
- "2.7"
- "3.3"
- "3.4"
- "3.5"
# - "pypy" - won't work as smmap doesn't work (see gitdb/.travis.yml for details)
-matrix:
- allow_failures:
- - python: "2.6"
git:
# a higher depth is needed for most of the tests - must be high enough to not actually be shallow
# as we clone our own repository in the process
@@ -17,7 +13,8 @@ install:
- python --version; git --version
- git submodule update --init --recursive
- git fetch --tags
- - pip install codecov flake8 ddt sphinx
+ - pip install -r test-requirements.txt
+ - pip install codecov sphinx
# generate some reflog as git-python tests need it (in master)
- ./init-tests-after-clone.sh
diff --git a/git/__init__.py b/git/__init__.py
index 58e4e7b6..0514d545 100644
--- a/git/__init__.py
+++ b/git/__init__.py
@@ -5,9 +5,12 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
# flake8: noqa
#@PydevCodeAnalysisIgnore
+import inspect
import os
import sys
-import inspect
+
+import os.path as osp
+
__version__ = 'git'
@@ -16,7 +19,7 @@ __version__ = 'git'
def _init_externals():
"""Initialize external projects by putting them into the path"""
if __version__ == 'git':
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'ext', 'gitdb'))
+ sys.path.insert(0, osp.join(osp.dirname(__file__), 'ext', 'gitdb'))
try:
import gitdb
diff --git a/git/cmd.py b/git/cmd.py
index ddce9824..72ba82c3 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -31,6 +31,7 @@ from git.compat import (
)
from git.exc import CommandError
from git.odict import OrderedDict
+from git.util import is_cygwin_git, cygpath
from .exc import (
GitCommandError,
@@ -191,8 +192,26 @@ class Git(LazyMixin):
USE_SHELL = False
@classmethod
- def polish_url(cls, url):
- return url.replace("\\\\", "\\").replace("\\", "/")
+ def is_cygwin(cls):
+ return is_cygwin_git(cls.GIT_PYTHON_GIT_EXECUTABLE)
+
+ @classmethod
+ def polish_url(cls, url, is_cygwin=None):
+ if is_cygwin is None:
+ is_cygwin = cls.is_cygwin()
+
+ if is_cygwin:
+ url = cygpath(url)
+ else:
+ """Remove any backslahes from urls to be written in config files.
+
+ Windows might create config-files containing paths with backslashed,
+ but git stops liking them as it will escape the backslashes.
+ Hence we undo the escaping just to be sure.
+ """
+ url = url.replace("\\\\", "\\").replace("\\", "/")
+
+ return url
class AutoInterrupt(object):
"""Kill/Interrupt the stored process instance once this instance goes out of scope. It is
diff --git a/git/config.py b/git/config.py
index 00943b58..f530bac8 100644
--- a/git/config.py
+++ b/git/config.py
@@ -6,21 +6,13 @@
"""Module containing module parser implementation able to properly read and write
configuration files"""
-import re
-try:
- import ConfigParser as cp
-except ImportError:
- # PY3
- import configparser as cp
+import abc
+from functools import wraps
import inspect
import logging
-import abc
import os
+import re
-from functools import wraps
-
-from git.odict import OrderedDict
-from git.util import LockFile
from git.compat import (
string_types,
FileType,
@@ -29,6 +21,18 @@ from git.compat import (
with_metaclass,
PY3
)
+from git.odict import OrderedDict
+from git.util import LockFile
+
+import os.path as osp
+
+
+try:
+ import ConfigParser as cp
+except ImportError:
+ # PY3
+ import configparser as cp
+
__all__ = ('GitConfigParser', 'SectionConstraint')
@@ -408,15 +412,15 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje
if self._has_includes():
for _, include_path in self.items('include'):
if include_path.startswith('~'):
- include_path = os.path.expanduser(include_path)
- if not os.path.isabs(include_path):
+ include_path = osp.expanduser(include_path)
+ if not osp.isabs(include_path):
if not file_ok:
continue
# end ignore relative paths if we don't know the configuration file path
- assert os.path.isabs(file_path), "Need absolute paths to be sure our cycle checks will work"
- include_path = os.path.join(os.path.dirname(file_path), include_path)
+ assert osp.isabs(file_path), "Need absolute paths to be sure our cycle checks will work"
+ include_path = osp.join(osp.dirname(file_path), include_path)
# end make include path absolute
- include_path = os.path.normpath(include_path)
+ include_path = osp.normpath(include_path)
if include_path in seen or not os.access(include_path, os.R_OK):
continue
seen.add(include_path)
diff --git a/git/db.py b/git/db.py
index 39b9872a..653fa7da 100644
--- a/git/db.py
+++ b/git/db.py
@@ -1,12 +1,9 @@
"""Module with our own gitdb implementation - it uses the git command"""
+from git.util import bin_to_hex, hex_to_bin
from gitdb.base import (
OInfo,
OStream
)
-from gitdb.util import (
- bin_to_hex,
- hex_to_bin
-)
from gitdb.db import GitDB # @UnusedImport
from gitdb.db import LooseObjectDB
diff --git a/git/diff.py b/git/diff.py
index 96e246a2..16c782f3 100644
--- a/git/diff.py
+++ b/git/diff.py
@@ -5,18 +5,17 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
import re
-from gitdb.util import hex_to_bin
+from git.cmd import handle_process_output
+from git.compat import (
+ defenc,
+ PY3
+)
+from git.util import finalize_process, hex_to_bin
from .compat import binary_type
from .objects.blob import Blob
from .objects.util import mode_str_to_int
-from git.compat import (
- defenc,
- PY3
-)
-from git.cmd import handle_process_output
-from git.util import finalize_process
__all__ = ('Diffable', 'DiffIndex', 'Diff', 'NULL_TREE')
diff --git a/git/ext/gitdb b/git/ext/gitdb
-Subproject 38866bc7c4956170c681a62c4508f934ac82646
+Subproject 97035c64f429c229629c25becc54ae44dd95e49
diff --git a/git/index/base.py b/git/index/base.py
index c93a999b..80862882 100644
--- a/git/index/base.py
+++ b/git/index/base.py
@@ -3,34 +3,28 @@
#
# This module is part of GitPython and is released under
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
-import tempfile
-import os
-import sys
-import subprocess
import glob
from io import BytesIO
-
+import os
from stat import S_ISLNK
+import subprocess
+import sys
+import tempfile
-from .typ import (
- BaseIndexEntry,
- IndexEntry,
-)
-
-from .util import (
- TemporaryFileSwap,
- post_clear_cache,
- default_index,
- git_working_dir
+from git.compat import (
+ izip,
+ xrange,
+ string_types,
+ force_bytes,
+ defenc,
+ mviter,
+ is_win
)
-
-import git.diff as diff
from git.exc import (
GitCommandError,
CheckoutError,
InvalidGitRepositoryError
)
-
from git.objects import (
Blob,
Submodule,
@@ -38,26 +32,21 @@ from git.objects import (
Object,
Commit,
)
-
from git.objects.util import Serializable
-from git.compat import (
- izip,
- xrange,
- string_types,
- force_bytes,
- defenc,
- mviter,
- is_win
-)
-
from git.util import (
LazyMixin,
LockedFD,
join_path_native,
file_contents_ro,
to_native_path_linux,
- unbare_repo
+ unbare_repo,
+ to_bin_sha
)
+from gitdb.base import IStream
+from gitdb.db import MemoryDB
+
+import git.diff as diff
+import os.path as osp
from .fun import (
entry_key,
@@ -69,10 +58,17 @@ from .fun import (
S_IFGITLINK,
run_commit_hook
)
+from .typ import (
+ BaseIndexEntry,
+ IndexEntry,
+)
+from .util import (
+ TemporaryFileSwap,
+ post_clear_cache,
+ default_index,
+ git_working_dir
+)
-from gitdb.base import IStream
-from gitdb.db import MemoryDB
-from gitdb.util import to_bin_sha
__all__ = ('IndexFile', 'CheckoutError')
@@ -354,7 +350,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
index.entries # force it to read the file as we will delete the temp-file
del(index_handler) # release as soon as possible
finally:
- if os.path.exists(tmp_index):
+ if osp.exists(tmp_index):
os.remove(tmp_index)
# END index merge handling
@@ -374,8 +370,8 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
rs = r + os.sep
for path in paths:
abs_path = path
- if not os.path.isabs(abs_path):
- abs_path = os.path.join(r, path)
+ if not osp.isabs(abs_path):
+ abs_path = osp.join(r, path)
# END make absolute path
try:
@@ -407,7 +403,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
for root, dirs, files in os.walk(abs_path, onerror=raise_exc): # @UnusedVariable
for rela_file in files:
# add relative paths only
- yield os.path.join(root.replace(rs, ''), rela_file)
+ yield osp.join(root.replace(rs, ''), rela_file)
# END for each file in subdir
# END for each subdirectory
except OSError:
@@ -569,7 +565,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
def _to_relative_path(self, path):
""":return: Version of path relative to our git directory or raise ValueError
if it is not within our git direcotory"""
- if not os.path.isabs(path):
+ if not osp.isabs(path):
return path
if self.repo.bare:
raise InvalidGitRepositoryError("require non-bare repository")
@@ -617,12 +613,12 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
entries_added = list()
if path_rewriter:
for path in paths:
- if os.path.isabs(path):
+ if osp.isabs(path):
abspath = path
gitrelative_path = path[len(self.repo.working_tree_dir) + 1:]
else:
gitrelative_path = path
- abspath = os.path.join(self.repo.working_tree_dir, gitrelative_path)
+ abspath = osp.join(self.repo.working_tree_dir, gitrelative_path)
# end obtain relative and absolute paths
blob = Blob(self.repo, Blob.NULL_BIN_SHA,
diff --git a/git/index/fun.py b/git/index/fun.py
index ddb04138..7f7518e1 100644
--- a/git/index/fun.py
+++ b/git/index/fun.py
@@ -1,6 +1,8 @@
# Contains standalone functions to accompany the index implementation and make it
# more versatile
# NOTE: Autodoc hates it if this is a docstring
+from io import BytesIO
+import os
from stat import (
S_IFDIR,
S_IFLNK,
@@ -9,13 +11,18 @@ from stat import (
S_IFMT,
S_IFREG,
)
-
-from io import BytesIO
-import os
import subprocess
-from git.util import IndexFileSHA1Writer, finalize_process
from git.cmd import PROC_CREATIONFLAGS, handle_process_output
+from git.compat import (
+ PY3,
+ defenc,
+ force_text,
+ force_bytes,
+ is_posix,
+ safe_encode,
+ safe_decode,
+)
from git.exc import (
UnmergedEntriesError,
HookExecutionError
@@ -25,6 +32,11 @@ from git.objects.fun import (
traverse_tree_recursive,
traverse_trees_recursive
)
+from git.util import IndexFileSHA1Writer, finalize_process
+from gitdb.base import IStream
+from gitdb.typ import str_tree_type
+
+import os.path as osp
from .typ import (
BaseIndexEntry,
@@ -32,23 +44,11 @@ from .typ import (
CE_NAMEMASK,
CE_STAGESHIFT
)
-
from .util import (
pack,
unpack
)
-from gitdb.base import IStream
-from gitdb.typ import str_tree_type
-from git.compat import (
- PY3,
- defenc,
- force_text,
- force_bytes,
- is_posix,
- safe_encode,
- safe_decode,
-)
S_IFGITLINK = S_IFLNK | S_IFDIR # a submodule
CE_NAMEMASK_INV = ~CE_NAMEMASK
@@ -59,7 +59,7 @@ __all__ = ('write_cache', 'read_cache', 'write_tree_from_cache', 'entry_key',
def hook_path(name, git_dir):
""":return: path to the given named hook in the given git repository directory"""
- return os.path.join(git_dir, 'hooks', name)
+ return osp.join(git_dir, 'hooks', name)
def run_commit_hook(name, index):
diff --git a/git/index/util.py b/git/index/util.py
index 5d72a9cc..02742a5d 100644
--- a/git/index/util.py
+++ b/git/index/util.py
@@ -1,12 +1,14 @@
"""Module containing index utilities"""
+from functools import wraps
+import os
import struct
import tempfile
-import os
-
-from functools import wraps
from git.compat import is_win
+import os.path as osp
+
+
__all__ = ('TemporaryFileSwap', 'post_clear_cache', 'default_index', 'git_working_dir')
#{ Aliases
@@ -32,8 +34,8 @@ class TemporaryFileSwap(object):
pass
def __del__(self):
- if os.path.isfile(self.tmp_file_path):
- if is_win and os.path.exists(self.file_path):
+ if osp.isfile(self.tmp_file_path):
+ if is_win and osp.exists(self.file_path):
os.remove(self.file_path)
os.rename(self.tmp_file_path, self.file_path)
# END temp file exists
diff --git a/git/objects/base.py b/git/objects/base.py
index 8027299c..4f078e38 100644
--- a/git/objects/base.py
+++ b/git/objects/base.py
@@ -3,14 +3,13 @@
#
# This module is part of GitPython and is released under
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
-from .util import get_object_type_by_name
-from git.util import LazyMixin, join_path_native, stream_copy
-from gitdb.util import (
- bin_to_hex,
- basename
-)
+from git.util import LazyMixin, join_path_native, stream_copy, bin_to_hex
import gitdb.typ as dbtyp
+import os.path as osp
+
+from .util import get_object_type_by_name
+
_assertion_msg_format = "Created object %r whose python type %r disagrees with the acutal git object type %r"
@@ -170,7 +169,7 @@ class IndexObject(Object):
@property
def name(self):
""":return: Name portion of the path, effectively being the basename"""
- return basename(self.path)
+ return osp.basename(self.path)
@property
def abspath(self):
diff --git a/git/objects/commit.py b/git/objects/commit.py
index e537c0bb..633f7aa1 100644
--- a/git/objects/commit.py
+++ b/git/objects/commit.py
@@ -5,8 +5,8 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
from gitdb import IStream
-from gitdb.util import hex_to_bin
from git.util import (
+ hex_to_bin,
Actor,
Iterable,
Stats,
diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py
index 2e265a54..18988b97 100644
--- a/git/objects/submodule/base.py
+++ b/git/objects/submodule/base.py
@@ -1,21 +1,18 @@
-from .util import (
- mkhead,
- sm_name,
- sm_section,
- SubmoduleConfigParser,
- find_first_remote_branch
-)
-from git.objects.util import Traversable
-from io import BytesIO # need a dict to set bloody .name field
-from git.util import (
- Iterable,
- join_path_native,
- to_native_path_linux,
- RemoteProgress,
- rmtree,
- unbare_repo
-)
+# need a dict to set bloody .name field
+from io import BytesIO
+import logging
+import os
+import stat
+from unittest.case import SkipTest
+import uuid
+import git
+from git.cmd import Git
+from git.compat import (
+ string_types,
+ defenc,
+ is_win,
+)
from git.config import (
SectionConstraint,
GitConfigParser,
@@ -26,22 +23,28 @@ from git.exc import (
NoSuchPathError,
RepositoryDirtyError
)
-from git.compat import (
- string_types,
- defenc,
- is_win,
+from git.objects.base import IndexObject, Object
+from git.objects.util import Traversable
+from git.util import (
+ Iterable,
+ join_path_native,
+ to_native_path_linux,
+ RemoteProgress,
+ rmtree,
+ unbare_repo
)
+from git.util import HIDE_WINDOWS_KNOWN_ERRORS
-import stat
-import git
+import os.path as osp
+
+from .util import (
+ mkhead,
+ sm_name,
+ sm_section,
+ SubmoduleConfigParser,
+ find_first_remote_branch
+)
-import os
-import logging
-import uuid
-from unittest.case import SkipTest
-from git.util import HIDE_WINDOWS_KNOWN_ERRORS
-from git.objects.base import IndexObject, Object
-from git.cmd import Git
__all__ = ["Submodule", "UpdateProgress"]
@@ -120,7 +123,7 @@ class Submodule(IndexObject, Iterable, Traversable):
self.path = reader.get_value('path')
except cp.NoSectionError:
raise ValueError("This submodule instance does not exist anymore in '%s' file"
- % os.path.join(self.repo.working_tree_dir, '.gitmodules'))
+ % osp.join(self.repo.working_tree_dir, '.gitmodules'))
# end
self._url = reader.get_value('url')
# git-python extension values - optional
@@ -181,7 +184,7 @@ class Submodule(IndexObject, Iterable, Traversable):
# end handle parent_commit
if not repo.bare and parent_matches_head:
- fp_module = os.path.join(repo.working_tree_dir, cls.k_modules_file)
+ fp_module = osp.join(repo.working_tree_dir, cls.k_modules_file)
else:
assert parent_commit is not None, "need valid parent_commit in bare repositories"
try:
@@ -229,9 +232,9 @@ class Submodule(IndexObject, Iterable, Traversable):
@classmethod
def _module_abspath(cls, parent_repo, path, name):
if cls._need_gitfile_submodules(parent_repo.git):
- return os.path.join(parent_repo.git_dir, 'modules', name)
+ return osp.join(parent_repo.git_dir, 'modules', name)
else:
- return os.path.join(parent_repo.working_tree_dir, path)
+ return osp.join(parent_repo.working_tree_dir, path)
# end
@classmethod
@@ -246,10 +249,10 @@ class Submodule(IndexObject, Iterable, Traversable):
module_checkout_path = module_abspath
if cls._need_gitfile_submodules(repo.git):
kwargs['separate_git_dir'] = module_abspath
- module_abspath_dir = os.path.dirname(module_abspath)
- if not os.path.isdir(module_abspath_dir):
+ module_abspath_dir = osp.dirname(module_abspath)
+ if not osp.isdir(module_abspath_dir):
os.makedirs(module_abspath_dir)
- module_checkout_path = os.path.join(repo.working_tree_dir, path)
+ module_checkout_path = osp.join(repo.working_tree_dir, path)
# end
clone = git.Repo.clone_from(url, module_checkout_path, **kwargs)
@@ -267,7 +270,7 @@ class Submodule(IndexObject, Iterable, Traversable):
path = path[:-1]
# END handle trailing slash
- if os.path.isabs(path):
+ if osp.isabs(path):
working_tree_linux = to_native_path_linux(parent_repo.working_tree_dir)
if not path.startswith(working_tree_linux):
raise ValueError("Submodule checkout path '%s' needs to be within the parents repository at '%s'"
@@ -291,18 +294,18 @@ class Submodule(IndexObject, Iterable, Traversable):
:param working_tree_dir: directory to write the .git file into
:param module_abspath: absolute path to the bare repository
"""
- git_file = os.path.join(working_tree_dir, '.git')
- rela_path = os.path.relpath(module_abspath, start=working_tree_dir)
+ git_file = osp.join(working_tree_dir, '.git')
+ rela_path = osp.relpath(module_abspath, start=working_tree_dir)
if is_win:
- if os.path.isfile(git_file):
+ if osp.isfile(git_file):
os.remove(git_file)
with open(git_file, 'wb') as fp:
fp.write(("gitdir: %s" % rela_path).encode(defenc))
- with GitConfigParser(os.path.join(module_abspath, 'config'),
+ with GitConfigParser(osp.join(module_abspath, 'config'),
read_only=False, merge_includes=False) as writer:
writer.set_value('core', 'worktree',
- to_native_path_linux(os.path.relpath(working_tree_dir, start=module_abspath)))
+ to_native_path_linux(osp.relpath(working_tree_dir, start=module_abspath)))
#{ Edit Interface
@@ -501,7 +504,7 @@ class Submodule(IndexObject, Iterable, Traversable):
# there is no git-repository yet - but delete empty paths
checkout_module_abspath = self.abspath
- if not dry_run and os.path.isdir(checkout_module_abspath):
+ if not dry_run and osp.isdir(checkout_module_abspath):
try:
os.rmdir(checkout_module_abspath)
except OSError:
@@ -671,7 +674,7 @@ class Submodule(IndexObject, Iterable, Traversable):
# END handle no change
module_checkout_abspath = join_path_native(self.repo.working_tree_dir, module_checkout_path)
- if os.path.isfile(module_checkout_abspath):
+ if osp.isfile(module_checkout_abspath):
raise ValueError("Cannot move repository onto a file: %s" % module_checkout_abspath)
# END handle target files
@@ -684,12 +687,12 @@ class Submodule(IndexObject, Iterable, Traversable):
# remove existing destination
if module:
- if os.path.exists(module_checkout_abspath):
+ if osp.exists(module_checkout_abspath):
if len(os.listdir(module_checkout_abspath)):
raise ValueError("Destination module directory was not empty")
# END handle non-emptiness
- if os.path.islink(module_checkout_abspath):
+ if osp.islink(module_checkout_abspath):
os.remove(module_checkout_abspath)
else:
os.rmdir(module_checkout_abspath)
@@ -704,11 +707,11 @@ class Submodule(IndexObject, Iterable, Traversable):
# move the module into place if possible
cur_path = self.abspath
renamed_module = False
- if module and os.path.exists(cur_path):
+ if module and osp.exists(cur_path):
os.renames(cur_path, module_checkout_abspath)
renamed_module = True
- if os.path.isfile(os.path.join(module_checkout_abspath, '.git')):
+ if osp.isfile(osp.join(module_checkout_abspath, '.git')):
module_abspath = self._module_abspath(self.repo, self.path, self.name)
self._write_git_file_and_module_config(module_checkout_abspath, module_abspath)
# end handle git file rewrite
@@ -804,11 +807,11 @@ class Submodule(IndexObject, Iterable, Traversable):
# state. Delete the .git folders last, start with the submodules first
mp = self.abspath
method = None
- if os.path.islink(mp):
+ if osp.islink(mp):
method = os.remove
- elif os.path.isdir(mp):
+ elif osp.isdir(mp):
method = rmtree
- elif os.path.exists(mp):
+ elif osp.exists(mp):
raise AssertionError("Cannot forcibly delete repository as it was neither a link, nor a directory")
# END handle brutal deletion
if not dry_run:
@@ -865,7 +868,7 @@ class Submodule(IndexObject, Iterable, Traversable):
# END delete tree if possible
# END handle force
- if not dry_run and os.path.isdir(git_dir):
+ if not dry_run and osp.isdir(git_dir):
self._clear_cache()
try:
rmtree(git_dir)
diff --git a/git/objects/tag.py b/git/objects/tag.py
index cefff083..19cb04bf 100644
--- a/git/objects/tag.py
+++ b/git/objects/tag.py
@@ -5,12 +5,9 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
""" Module containing all object based types. """
from . import base
-from .util import (
- get_object_type_by_name,
- parse_actor_and_date
-)
-from gitdb.util import hex_to_bin
-from git.compat import defenc
+from .util import get_object_type_by_name, parse_actor_and_date
+from ..util import hex_to_bin
+from ..compat import defenc
__all__ = ("TagObject", )
diff --git a/git/objects/tree.py b/git/objects/tree.py
index 18c0add1..ed7c2435 100644
--- a/git/objects/tree.py
+++ b/git/objects/tree.py
@@ -5,7 +5,7 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
from git.util import join_path
import git.diff as diff
-from gitdb.util import to_bin_sha
+from git.util import to_bin_sha
from . import util
from .base import IndexObject
@@ -18,7 +18,7 @@ from .fun import (
tree_to_stream
)
-from gitdb.utils.compat import PY3
+from git.compat import PY3
if PY3:
cmp = lambda a, b: (a > b) - (a < b)
diff --git a/git/refs/log.py b/git/refs/log.py
index 3078355d..bab6ae04 100644
--- a/git/refs/log.py
+++ b/git/refs/log.py
@@ -1,31 +1,29 @@
+import re
+import time
+
+from git.compat import (
+ PY3,
+ xrange,
+ string_types,
+ defenc
+)
+from git.objects.util import (
+ parse_date,
+ Serializable,
+ altz_to_utctz_str,
+)
from git.util import (
Actor,
LockedFD,
LockFile,
assure_directory_exists,
to_native_path,
-)
-
-from gitdb.util import (
bin_to_hex,
- join,
- file_contents_ro_filepath,
+ file_contents_ro_filepath
)
-from git.objects.util import (
- parse_date,
- Serializable,
- altz_to_utctz_str,
-)
-from git.compat import (
- PY3,
- xrange,
- string_types,
- defenc
-)
+import os.path as osp
-import time
-import re
__all__ = ["RefLog", "RefLogEntry"]
@@ -185,7 +183,7 @@ class RefLog(list, Serializable):
instance would be found. The path is not guaranteed to point to a valid
file though.
:param ref: SymbolicReference instance"""
- return join(ref.repo.git_dir, "logs", to_native_path(ref.path))
+ return osp.join(ref.repo.git_dir, "logs", to_native_path(ref.path))
@classmethod
def iter_entries(cls, stream):
diff --git a/git/refs/remote.py b/git/refs/remote.py
index b5318bc3..ef69b5db 100644
--- a/git/refs/remote.py
+++ b/git/refs/remote.py
@@ -1,9 +1,10 @@
+import os
+
from git.util import join_path
-from gitdb.util import join
-from .head import Head
+import os.path as osp
-import os
+from .head import Head
__all__ = ["RemoteReference"]
@@ -36,7 +37,7 @@ class RemoteReference(Head):
# and delete remainders manually
for ref in refs:
try:
- os.remove(join(repo.git_dir, ref.path))
+ os.remove(osp.join(repo.git_dir, ref.path))
except OSError:
pass
# END for each ref
diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
index 00fb2fcd..3c6b78e5 100644
--- a/git/refs/symbolic.py
+++ b/git/refs/symbolic.py
@@ -1,34 +1,28 @@
import os
+from git.compat import (
+ string_types,
+ defenc
+)
from git.objects import Object, Commit
from git.util import (
join_path,
join_path_native,
to_native_path_linux,
- assure_directory_exists
+ assure_directory_exists,
+ hex_to_bin,
+ LockedFD
)
-
from gitdb.exc import (
BadObject,
BadName
)
-from gitdb.util import (
- join,
- dirname,
- isdir,
- exists,
- isfile,
- rename,
- hex_to_bin,
- LockedFD
-)
-from git.compat import (
- string_types,
- defenc
-)
+
+import os.path as osp
from .log import RefLog
+
__all__ = ["SymbolicReference"]
@@ -81,7 +75,7 @@ class SymbolicReference(object):
@classmethod
def _get_packed_refs_path(cls, repo):
- return join(repo.git_dir, 'packed-refs')
+ return osp.join(repo.git_dir, 'packed-refs')
@classmethod
def _iter_packed_refs(cls, repo):
@@ -134,7 +128,7 @@ class SymbolicReference(object):
point to, or None"""
tokens = None
try:
- with open(join(repo.git_dir, ref_path), 'rt') as fp:
+ with open(osp.join(repo.git_dir, ref_path), 'rt') as fp:
value = fp.read().rstrip()
# Don't only split on spaces, but on whitespace, which allows to parse lines like
# 60b64ef992065e2600bfef6187a97f92398a9144 branch 'master' of git-server:/path/to/repo
@@ -418,8 +412,8 @@ class SymbolicReference(object):
or just "myreference", hence 'refs/' is implied.
Alternatively the symbolic reference to be deleted"""
full_ref_path = cls.to_full_path(path)
- abs_path = join(repo.git_dir, full_ref_path)
- if exists(abs_path):
+ abs_path = osp.join(repo.git_dir, full_ref_path)
+ if osp.exists(abs_path):
os.remove(abs_path)
else:
# check packed refs
@@ -458,7 +452,7 @@ class SymbolicReference(object):
# delete the reflog
reflog_path = RefLog.path(cls(repo, full_ref_path))
- if os.path.isfile(reflog_path):
+ if osp.isfile(reflog_path):
os.remove(reflog_path)
# END remove reflog
@@ -470,14 +464,14 @@ class SymbolicReference(object):
corresponding object and a detached symbolic reference will be created
instead"""
full_ref_path = cls.to_full_path(path)
- abs_ref_path = join(repo.git_dir, full_ref_path)
+ abs_ref_path = osp.join(repo.git_dir, full_ref_path)
# figure out target data
target = reference
if resolve:
target = repo.rev_parse(str(reference))
- if not force and isfile(abs_ref_path):
+ if not force and osp.isfile(abs_ref_path):
target_data = str(target)
if isinstance(target, SymbolicReference):
target_data = target.path
@@ -544,9 +538,9 @@ class SymbolicReference(object):
if self.path == new_path:
return self
- new_abs_path = join(self.repo.git_dir, new_path)
- cur_abs_path = join(self.repo.git_dir, self.path)
- if isfile(new_abs_path):
+ new_abs_path = osp.join(self.repo.git_dir, new_path)
+ cur_abs_path = osp.join(self.repo.git_dir, self.path)
+ if osp.isfile(new_abs_path):
if not force:
# if they point to the same file, its not an error
with open(new_abs_path, 'rb') as fd1:
@@ -561,12 +555,12 @@ class SymbolicReference(object):
os.remove(new_abs_path)
# END handle existing target file
- dname = dirname(new_abs_path)
- if not isdir(dname):
+ dname = osp.dirname(new_abs_path)
+ if not osp.isdir(dname):
os.makedirs(dname)
# END create directory
- rename(cur_abs_path, new_abs_path)
+ os.rename(cur_abs_path, new_abs_path)
self.path = new_path
return self
diff --git a/git/remote.py b/git/remote.py
index 7cb7fd59..c682837e 100644
--- a/git/remote.py
+++ b/git/remote.py
@@ -5,8 +5,25 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
# Module implementing a remote object allowing easy access to git remotes
+import logging
import re
+from git.cmd import handle_process_output, Git
+from git.compat import (defenc, force_text, is_win)
+from git.exc import GitCommandError
+from git.util import (
+ LazyMixin,
+ Iterable,
+ IterableList,
+ RemoteProgress,
+ CallableRemoteProgress
+)
+from git.util import (
+ join_path,
+)
+
+import os.path as osp
+
from .config import (
SectionConstraint,
cp,
@@ -18,21 +35,7 @@ from .refs import (
SymbolicReference,
TagReference
)
-from git.util import (
- LazyMixin,
- Iterable,
- IterableList,
- RemoteProgress,
- CallableRemoteProgress
-)
-from git.util import (
- join_path,
-)
-from git.cmd import handle_process_output, Git
-from gitdb.util import join
-from git.compat import (defenc, force_text, is_win)
-import logging
-from git.exc import GitCommandError
+
log = logging.getLogger('git.remote')
@@ -644,7 +647,7 @@ class Remote(LazyMixin, Iterable):
continue
# read head information
- with open(join(self.repo.git_dir, 'FETCH_HEAD'), 'rb') as fp:
+ with open(osp.join(self.repo.git_dir, 'FETCH_HEAD'), 'rb') as fp:
fetch_head_info = [l.decode(defenc) for l in fp.readlines()]
l_fil = len(fetch_info_lines)
diff --git a/git/repo/base.py b/git/repo/base.py
index c5cdce7c..21d129e9 100644
--- a/git/repo/base.py
+++ b/git/repo/base.py
@@ -4,52 +4,16 @@
# This module is part of GitPython and is released under
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
-from git.exc import (
- InvalidGitRepositoryError,
- NoSuchPathError,
- GitCommandError
-)
+from collections import namedtuple
+import logging
+import os
+import re
+import sys
+
from git.cmd import (
Git,
handle_process_output
)
-from git.refs import (
- HEAD,
- Head,
- Reference,
- TagReference,
-)
-from git.objects import (
- Submodule,
- RootModule,
- Commit
-)
-from git.util import (
- Actor,
- finalize_process
-)
-from git.index import IndexFile
-from git.config import GitConfigParser
-from git.remote import (
- Remote,
- add_progress,
- to_progress_instance
-)
-
-from git.db import GitCmdObjectDB
-
-from gitdb.util import (
- join,
- isfile,
- hex_to_bin
-)
-
-from .fun import (
- rev_parse,
- is_git_dir,
- find_git_dir,
- touch,
-)
from git.compat import (
text_type,
defenc,
@@ -58,12 +22,19 @@ from git.compat import (
range,
is_win,
)
+from git.config import GitConfigParser
+from git.db import GitCmdObjectDB
+from git.exc import InvalidGitRepositoryError, NoSuchPathError, GitCommandError
+from git.index import IndexFile
+from git.objects import Submodule, RootModule, Commit
+from git.refs import HEAD, Head, Reference, TagReference
+from git.remote import Remote, add_progress, to_progress_instance
+from git.util import Actor, finalize_process, decygpath, hex_to_bin
+
+import os.path as osp
+
+from .fun import rev_parse, is_git_dir, find_git_dir, touch
-import os
-import sys
-import re
-import logging
-from collections import namedtuple
log = logging.getLogger(__name__)
@@ -79,7 +50,7 @@ __all__ = ('Repo',)
def _expand_path(p):
- return os.path.abspath(os.path.expandvars(os.path.expanduser(p)))
+ return osp.abspath(osp.expandvars(osp.expanduser(p)))
class Repo(object):
@@ -124,6 +95,8 @@ class Repo(object):
repo = Repo("~/Development/git-python.git")
repo = Repo("$REPOSITORIES/Development/git-python.git")
+ In *Cygwin*, path may be a `'cygdrive/...'` prefixed path.
+
:param odbt:
Object DataBase type - a type which is constructed by providing
the directory containing the database objects, i.e. .git/objects. It will
@@ -136,9 +109,12 @@ class Repo(object):
:raise InvalidGitRepositoryError:
:raise NoSuchPathError:
:return: git.Repo """
+ if Git.is_cygwin():
+ path = decygpath(path)
+
epath = _expand_path(path or os.getcwd())
self.git = None # should be set for __del__ not to fail in case we raise
- if not os.path.exists(epath):
+ if not osp.exists(epath):
raise NoSuchPathError(epath)
self.working_dir = None
@@ -148,24 +124,24 @@ class Repo(object):
# walk up the path to find the .git dir
while curpath:
- # ABOUT os.path.NORMPATH
+ # ABOUT osp.NORMPATH
# It's important to normalize the paths, as submodules will otherwise initialize their
# repo instances with paths that depend on path-portions that will not exist after being
# removed. It's just cleaner.
if is_git_dir(curpath):
- self.git_dir = os.path.normpath(curpath)
- self._working_tree_dir = os.path.dirname(self.git_dir)
+ self.git_dir = osp.normpath(curpath)
+ self._working_tree_dir = osp.dirname(self.git_dir)
break
- gitpath = find_git_dir(join(curpath, '.git'))
+ gitpath = find_git_dir(osp.join(curpath, '.git'))
if gitpath is not None:
- self.git_dir = os.path.normpath(gitpath)
+ self.git_dir = osp.normpath(gitpath)
self._working_tree_dir = curpath
break
if not search_parent_directories:
break
- curpath, dummy = os.path.split(curpath)
+ curpath, dummy = osp.split(curpath)
if not dummy:
break
# END while curpath
@@ -190,7 +166,7 @@ class Repo(object):
self.git = self.GitCommandWrapperType(self.working_dir)
# special handling, in special times
- args = [join(self.git_dir, 'objects')]
+ args = [osp.join(self.git_dir, 'objects')]
if issubclass(odbt, GitCmdObjectDB):
args.append(self.git)
self.odb = odbt(*args)
@@ -212,12 +188,12 @@ class Repo(object):
# Description property
def _get_description(self):
- filename = join(self.git_dir, 'description')
+ filename = osp.join(self.git_dir, 'description')
with open(filename, 'rb') as fp:
return fp.read().rstrip().decode(defenc)
def _set_description(self, descr):
- filename = join(self.git_dir, 'description')
+ filename = osp.join(self.git_dir, 'description')
with open(filename, 'wb') as fp:
fp.write((descr + '\n').encode(defenc))
@@ -381,12 +357,12 @@ class Repo(object):
if config_level == "system":
return "/etc/gitconfig"
elif config_level == "user":
- config_home = os.environ.get("XDG_CONFIG_HOME") or os.path.join(os.environ.get("HOME", '~'), ".config")
- return os.path.normpath(os.path.expanduser(join(config_home, "git", "config")))
+ config_home = os.environ.get("XDG_CONFIG_HOME") or osp.join(os.environ.get("HOME", '~'), ".config")
+ return osp.normpath(osp.expanduser(osp.join(config_home, "git", "config")))
elif config_level == "global":
- return os.path.normpath(os.path.expanduser("~/.gitconfig"))
+ return osp.normpath(osp.expanduser("~/.gitconfig"))
elif config_level == "repository":
- return os.path.normpath(join(self.git_dir, "config"))
+ return osp.normpath(osp.join(self.git_dir, "config"))
raise ValueError("Invalid configuration level: %r" % config_level)
@@ -530,12 +506,12 @@ class Repo(object):
return True
def _get_daemon_export(self):
- filename = join(self.git_dir, self.DAEMON_EXPORT_FILE)
- return os.path.exists(filename)
+ filename = osp.join(self.git_dir, self.DAEMON_EXPORT_FILE)
+ return osp.exists(filename)
def _set_daemon_export(self, value):
- filename = join(self.git_dir, self.DAEMON_EXPORT_FILE)
- fileexists = os.path.exists(filename)
+ filename = osp.join(self.git_dir, self.DAEMON_EXPORT_FILE)
+ fileexists = osp.exists(filename)
if value and not fileexists:
touch(filename)
elif not value and fileexists:
@@ -550,9 +526,9 @@ class Repo(object):
"""The list of alternates for this repo from which objects can be retrieved
:return: list of strings being pathnames of alternates"""
- alternates_path = join(self.git_dir, 'objects', 'info', 'alternates')
+ alternates_path = osp.join(self.git_dir, 'objects', 'info', 'alternates')
- if os.path.exists(alternates_path):
+ if osp.exists(alternates_path):
with open(alternates_path, 'rb') as f:
alts = f.read().decode(defenc)
return alts.strip().splitlines()
@@ -570,9 +546,9 @@ class Repo(object):
:note:
The method does not check for the existance of the paths in alts
as the caller is responsible."""
- alternates_path = join(self.git_dir, 'objects', 'info', 'alternates')
+ alternates_path = osp.join(self.git_dir, 'objects', 'info', 'alternates')
if not alts:
- if isfile(alternates_path):
+ if osp.isfile(alternates_path):
os.remove(alternates_path)
else:
with open(alternates_path, 'wb') as f:
@@ -601,7 +577,7 @@ class Repo(object):
default_args.append(path)
if index:
# diff index against HEAD
- if isfile(self.index.path) and \
+ if osp.isfile(self.index.path) and \
len(self.git.diff('--cached', *default_args)):
return True
# END index handling
@@ -861,7 +837,7 @@ class Repo(object):
:return: ``git.Repo`` (the newly created repo)"""
if path:
path = _expand_path(path)
- if mkdir and path and not os.path.exists(path):
+ if mkdir and path and not osp.exists(path):
os.makedirs(path, 0o755)
# git command automatically chdir into the directory
@@ -875,19 +851,32 @@ class Repo(object):
progress = to_progress_instance(progress)
odbt = kwargs.pop('odbt', odb_default_type)
- proc = git.clone(url, path, with_extended_output=True, as_process=True,
+
+ ## 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
+ # becomes::
+ # git clone --bare /cygwin/d/foo.git /cygwin/d/C:\\Work
+ #
+ clone_path = (Git.polish_url(path)
+ if Git.is_cygwin() and 'bare'in kwargs
+ else path)
+ sep_dir = kwargs.get('separate_git_dir')
+ 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))
if progress:
handle_process_output(proc, None, progress.new_message_handler(), finalize_process)
else:
- (stdout, stderr) = proc.communicate() # FIXME: Will block of outputs are big!
+ (stdout, stderr) = proc.communicate() # FIXME: Will block if outputs are big!
log.debug("Cmd(%s)'s unused stdout: %s", getattr(proc, 'args', ''), stdout)
finalize_process(proc, stderr=stderr)
# our git command could have a different working dir than our actual
# environment, hence we prepend its working dir if required
- if not os.path.isabs(path) and git.working_dir:
- path = join(git._working_dir, path)
+ if not osp.isabs(path) and git.working_dir:
+ path = osp.join(git._working_dir, path)
# adjust remotes - there may be operating systems which use backslashes,
# These might be given as initial paths, but when handling the config file
@@ -965,7 +954,7 @@ class Repo(object):
"""
if self.bare:
return False
- return os.path.isfile(os.path.join(self.working_tree_dir, '.git'))
+ return osp.isfile(osp.join(self.working_tree_dir, '.git'))
rev_parse = rev_parse
diff --git a/git/repo/fun.py b/git/repo/fun.py
index 4d852cfe..c5a8a52b 100644
--- a/git/repo/fun.py
+++ b/git/repo/fun.py
@@ -2,22 +2,18 @@
import os
from string import digits
+from git.compat import xrange
+from git.exc import WorkTreeRepositoryUnsupported
+from git.objects import Object
+from git.refs import SymbolicReference
+from git.util import hex_to_bin, bin_to_hex, decygpath
from gitdb.exc import (
BadObject,
BadName,
)
-from git.refs import SymbolicReference
-from git.objects import Object
-from gitdb.util import (
- join,
- isdir,
- isfile,
- dirname,
- hex_to_bin,
- bin_to_hex
-)
-from git.exc import WorkTreeRepositoryUnsupported
-from git.compat import xrange
+
+import os.path as osp
+from git.cmd import Git
__all__ = ('rev_parse', 'is_git_dir', 'touch', 'find_git_dir', 'name_to_object', 'short_to_long', 'deref_tag',
@@ -38,13 +34,15 @@ def is_git_dir(d):
but at least clearly indicates that we don't support it.
There is the unlikely danger to throw if we see directories which just look like a worktree dir,
but are none."""
- if isdir(d):
- if isdir(join(d, 'objects')) and isdir(join(d, 'refs')):
- headref = join(d, 'HEAD')
- return isfile(headref) or \
- (os.path.islink(headref) and
+ if osp.isdir(d):
+ if osp.isdir(osp.join(d, 'objects')) and osp.isdir(osp.join(d, 'refs')):
+ headref = osp.join(d, 'HEAD')
+ return osp.isfile(headref) or \
+ (osp.islink(headref) and
os.readlink(headref).startswith('refs'))
- elif isfile(join(d, 'gitdir')) and isfile(join(d, 'commondir')) and isfile(join(d, 'gitfile')):
+ elif (osp.isfile(osp.join(d, 'gitdir')) and
+ osp.isfile(osp.join(d, 'commondir')) and
+ osp.isfile(osp.join(d, 'gitfile'))):
raise WorkTreeRepositoryUnsupported(d)
return False
@@ -62,8 +60,11 @@ def find_git_dir(d):
else:
if content.startswith('gitdir: '):
path = content[8:]
- if not os.path.isabs(path):
- path = join(dirname(d), path)
+ if Git.is_cygwin():
+ ## Cygwin creates submodules prefixed with `/cygdrive/...` suffixes.
+ path = decygpath(path)
+ if not osp.isabs(path):
+ path = osp.join(osp.dirname(d), path)
return find_git_dir(path)
# end handle exception
return None
diff --git a/git/test/lib/helper.py b/git/test/lib/helper.py
index b009a9cd..1515f2a1 100644
--- a/git/test/lib/helper.py
+++ b/git/test/lib/helper.py
@@ -5,6 +5,7 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
from __future__ import print_function
+import contextlib
from functools import wraps
import io
import logging
@@ -16,7 +17,7 @@ from unittest import TestCase
import unittest
from git.compat import string_types, is_win, PY3
-from git.util import rmtree
+from git.util import rmtree, cwd
import os.path as osp
@@ -32,7 +33,7 @@ __all__ = (
'GIT_REPO', 'GIT_DAEMON_PORT'
)
-log = logging.getLogger('git.util')
+log = logging.getLogger(__name__)
#{ Routines
@@ -118,7 +119,7 @@ def with_rw_repo(working_tree_ref, bare=False):
if bare:
prefix = ''
# END handle prefix
- repo_dir = tempfile.mktemp("%sbare_%s" % (prefix, func.__name__))
+ repo_dir = tempfile.mktemp(prefix="%sbare_%s" % (prefix, func.__name__))
rw_repo = self.rorepo.clone(repo_dir, shared=True, bare=bare, n=True)
rw_repo.head.commit = rw_repo.commit(working_tree_ref)
@@ -151,32 +152,66 @@ def with_rw_repo(working_tree_ref, bare=False):
return argument_passer
-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. But it can use MINGW's.
- #
- daemon_cmd = ['git-daemon',
- '--enable=receive-pack',
- '--listen=%s' % ip,
- '--port=%s' % port,
- '--base-path=%s' % base_path,
- base_path]
- gd = Git().execute(daemon_cmd, as_process=True)
+@contextlib.contextmanager
+def git_daemon_launched(base_path, ip, port):
+ from git import Git # Avoid circular deps.
+
+ gd = None
+ try:
+ 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. But it can use MINGW's.
+ #
+ daemon_cmd = ['git-daemon',
+ '--enable=receive-pack',
+ '--listen=%s' % ip,
+ '--port=%s' % port,
+ '--base-path=%s' % base_path,
+ base_path]
+ gd = Git().execute(daemon_cmd, as_process=True)
+ else:
+ 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 * (1 + is_win))
+ except Exception as ex:
+ msg = textwrap.dedent("""
+ Launching git-daemon failed due to: %s
+ Probably test will fail subsequently.
+
+ BUT you may start *git-daemon* manually with this command:"
+ git daemon --enable=receive-pack --listen=%s --port=%s --base-path=%s %s
+ You may also run the daemon on a different port by passing --port=<port>"
+ and setting the environment variable GIT_PYTHON_TEST_GIT_DAEMON_PORT to <port>
+ """)
+ if is_win:
+ msg += textwrap.dedent("""
+
+ On Windows,
+ 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 (but has also paths problems).""")
+ log.warning(msg, ex, ip, port, base_path, base_path, exc_info=1)
+
+ yield # OK, assume daemon started manually.
+
else:
- 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
+ yield # Yield outside try, to avoid catching
+ finally:
+ if gd:
+ try:
+ log.debug("Killing git-daemon...")
+ gd.proc.kill()
+ except Exception as ex:
+ ## Either it has died (and we're here), or it won't die, again here...
+ log.debug("Hidden error while Killing git-daemon: %s", ex, exc_info=1)
def with_rw_and_rw_remote_repo(working_tree_ref):
@@ -196,7 +231,7 @@ def with_rw_and_rw_remote_repo(working_tree_ref):
rorepo ---<bare clone>---> rw_remote_repo ---<clone>---> rw_repo
The test case needs to support the following signature::
- def case(self, rw_repo, rw_remote_repo)
+ def case(self, rw_repo, rw_daemon_repo)
This setup allows you to test push and pull scenarios and hooks nicely.
@@ -211,94 +246,65 @@ def with_rw_and_rw_remote_repo(working_tree_ref):
@wraps(func)
def remote_repo_creator(self):
- remote_repo_dir = tempfile.mktemp("remote_repo_%s" % func.__name__)
- repo_dir = tempfile.mktemp("remote_clone_non_bare_repo")
+ rw_daemon_repo_dir = tempfile.mktemp(prefix="daemon_repo-%s-" % func.__name__)
+ rw_repo_dir = tempfile.mktemp(prefix="daemon_cloned_repo-%s-" % func.__name__)
- rw_remote_repo = self.rorepo.clone(remote_repo_dir, shared=True, bare=True)
+ rw_daemon_repo = self.rorepo.clone(rw_daemon_repo_dir, shared=True, bare=True)
# recursive alternates info ?
- rw_repo = rw_remote_repo.clone(repo_dir, shared=True, bare=False, n=True)
- rw_repo.head.commit = working_tree_ref
- rw_repo.head.reference.checkout()
-
- # prepare for git-daemon
- rw_remote_repo.daemon_export = True
-
- # this thing is just annoying !
- with rw_remote_repo.config_writer() as crw:
- section = "daemon"
- try:
- crw.add_section(section)
- except Exception:
- 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.
- d_remote = Remote.create(rw_repo, "daemon_origin", remote_repo_dir)
- d_remote.fetch()
-
- base_path, rel_repo_dir = osp.split(remote_repo_dir)
-
- remote_repo_url = Git.polish_url("git://localhost:%s/%s" % (GIT_DAEMON_PORT, rel_repo_dir))
- with d_remote.config_writer as cw:
- cw.set('url', remote_repo_url)
-
+ rw_repo = rw_daemon_repo.clone(rw_repo_dir, shared=True, bare=False, n=True)
try:
- 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=<port>"
- and setting the environment variable GIT_PYTHON_TEST_GIT_DAEMON_PORT to <port>
- """ % (base_path, base_path))
- raise AssertionError(ex, msg)
- # END make assertion
- else:
- # Try listing remotes, to diagnose whether the daemon is up.
- rw_repo.git.ls_remote(d_remote)
-
- # adjust working dir
- prev_cwd = os.getcwd()
- os.chdir(rw_repo.working_dir)
+ rw_repo.head.commit = working_tree_ref
+ rw_repo.head.reference.checkout()
- try:
- return func(self, rw_repo, rw_remote_repo)
- except:
- log.info("Keeping repos after failure: repo_dir = %s, remote_repo_dir = %s",
- repo_dir, remote_repo_dir)
- repo_dir = remote_repo_dir = None
- raise
- finally:
- os.chdir(prev_cwd)
+ # prepare for git-daemon
+ rw_daemon_repo.daemon_export = True
+
+ # this thing is just annoying !
+ with rw_daemon_repo.config_writer() as crw:
+ section = "daemon"
+ try:
+ crw.add_section(section)
+ except Exception:
+ 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.
+ d_remote = Remote.create(rw_repo, "daemon_origin", rw_daemon_repo_dir)
+ d_remote.fetch()
+
+ base_daemon_path, rel_repo_dir = osp.split(rw_daemon_repo_dir)
+
+ remote_repo_url = Git.polish_url("git://localhost:%s/%s" % (GIT_DAEMON_PORT, rel_repo_dir))
+ with d_remote.config_writer as cw:
+ cw.set('url', remote_repo_url)
+
+ with git_daemon_launched(Git.polish_url(base_daemon_path, is_cygwin=False), # No daemon in Cygwin.
+ '127.0.0.1',
+ GIT_DAEMON_PORT):
+ # Try listing remotes, to diagnose whether the daemon is up.
+ rw_repo.git.ls_remote(d_remote)
+
+ with cwd(rw_repo.working_dir):
+ try:
+ return func(self, rw_repo, rw_daemon_repo)
+ except:
+ log.info("Keeping repos after failure: \n rw_repo_dir: %s \n rw_daemon_repo_dir: %s",
+ rw_repo_dir, rw_daemon_repo_dir)
+ rw_repo_dir = rw_daemon_repo_dir = None
+ raise
finally:
- try:
- log.debug("Killing git-daemon...")
- gd.proc.kill()
- except:
- ## Either it has died (and we're here), or it won't die, again here...
- pass
-
rw_repo.git.clear_cache()
- rw_remote_repo.git.clear_cache()
- rw_repo = rw_remote_repo = None
+ rw_daemon_repo.git.clear_cache()
+ del rw_repo
+ del rw_daemon_repo
import gc
gc.collect()
- if repo_dir:
- rmtree(repo_dir)
- if remote_repo_dir:
- rmtree(remote_repo_dir)
-
- if gd is not None:
- gd.proc.wait()
+ if rw_repo_dir:
+ rmtree(rw_repo_dir)
+ if rw_daemon_repo_dir:
+ rmtree(rw_daemon_repo_dir)
# END cleanup
# END bare repo creator
return remote_repo_creator
diff --git a/git/test/performance/lib.py b/git/test/performance/lib.py
index ed82e4dd..7edffa78 100644
--- a/git/test/performance/lib.py
+++ b/git/test/performance/lib.py
@@ -1,22 +1,23 @@
"""Contains library functions"""
+import logging
import os
-from git.test.lib import (
- TestBase
-)
import tempfile
-import logging
+from git import (
+ Repo
+)
from git.db import (
GitCmdObjectDB,
GitDB
)
-
-from git import (
- Repo
+from git.test.lib import (
+ TestBase
)
from git.util import rmtree
+import os.path as osp
#{ Invariants
+
k_env_git_repo = "GIT_PYTHON_TEST_GIT_REPO_BASE"
#} END invariants
@@ -52,7 +53,7 @@ class TestBigRepoR(TestBase):
logging.info(
("You can set the %s environment variable to a .git repository of" % k_env_git_repo) +
"your choice - defaulting to the gitpython repository")
- repo_path = os.path.dirname(__file__)
+ repo_path = osp.dirname(__file__)
# end set some repo path
self.gitrorepo = Repo(repo_path, odbt=GitCmdObjectDB, search_parent_directories=True)
self.puregitrorepo = Repo(repo_path, odbt=GitDB, search_parent_directories=True)
diff --git a/git/test/performance/test_streams.py b/git/test/performance/test_streams.py
index 42cbade5..3909d8ff 100644
--- a/git/test/performance/test_streams.py
+++ b/git/test/performance/test_streams.py
@@ -1,26 +1,27 @@
"""Performance data streaming performance"""
from __future__ import print_function
-from time import time
import os
-import sys
import subprocess
+import sys
+from time import time
from git.test.lib import (
with_rw_repo
)
-from gitdb.util import bin_to_hex
+from git.util import bin_to_hex
+from gitdb import (
+ LooseObjectDB,
+ IStream
+)
from gitdb.test.lib import make_memory_file
+import os.path as osp
+
from .lib import (
TestBigRepoR
)
-from gitdb import (
- LooseObjectDB,
- IStream
-)
-
class TestObjDBPerformance(TestBigRepoR):
@@ -31,7 +32,7 @@ class TestObjDBPerformance(TestBigRepoR):
def test_large_data_streaming(self, rwrepo):
# TODO: This part overlaps with the same file in gitdb.test.performance.test_stream
# It should be shared if possible
- ldb = LooseObjectDB(os.path.join(rwrepo.git_dir, 'objects'))
+ ldb = LooseObjectDB(osp.join(rwrepo.git_dir, 'objects'))
for randomize in range(2):
desc = (randomize and 'random ') or ''
@@ -47,7 +48,7 @@ class TestObjDBPerformance(TestBigRepoR):
elapsed_add = time() - st
assert ldb.has_object(binsha)
db_file = ldb.readable_db_object_path(bin_to_hex(binsha))
- fsize_kib = os.path.getsize(db_file) / 1000
+ fsize_kib = osp.getsize(db_file) / 1000
size_kib = size / 1000
msg = "Added %i KiB (filesize = %i KiB) of %s data to loose odb in %f s ( %f Write KiB / s)"
@@ -109,7 +110,7 @@ class TestObjDBPerformance(TestBigRepoR):
assert gitsha == bin_to_hex(binsha) # we do it the same way, right ?
# as its the same sha, we reuse our path
- fsize_kib = os.path.getsize(db_file) / 1000
+ fsize_kib = osp.getsize(db_file) / 1000
msg = "Added %i KiB (filesize = %i KiB) of %s data to using git-hash-object in %f s ( %f Write KiB / s)"
msg %= (size_kib, fsize_kib, desc, gelapsed_add, size_kib / gelapsed_add)
print(msg, file=sys.stderr)
diff --git a/git/test/test_base.py b/git/test/test_base.py
index 7fc3096f..cec40de8 100644
--- a/git/test/test_base.py
+++ b/git/test/test_base.py
@@ -9,22 +9,24 @@ import sys
import tempfile
from unittest import skipIf
-import git.objects.base as base
-from git.test.lib import (
- TestBase,
- assert_raises,
- with_rw_repo,
- with_rw_and_rw_remote_repo
-)
from git import (
Blob,
Tree,
Commit,
TagObject
)
-from git.objects.util import get_object_type_by_name
-from gitdb.util import hex_to_bin
from git.compat import is_win
+from git.objects.util import get_object_type_by_name
+from git.test.lib import (
+ TestBase,
+ assert_raises,
+ with_rw_repo,
+ with_rw_and_rw_remote_repo
+)
+from git.util import hex_to_bin
+
+import git.objects.base as base
+import os.path as osp
class TestBase(TestBase):
@@ -103,19 +105,19 @@ class TestBase(TestBase):
@with_rw_repo('HEAD', bare=True)
def test_with_bare_rw_repo(self, bare_rw_repo):
assert bare_rw_repo.config_reader("repository").getboolean("core", "bare")
- assert os.path.isfile(os.path.join(bare_rw_repo.git_dir, 'HEAD'))
+ assert osp.isfile(osp.join(bare_rw_repo.git_dir, 'HEAD'))
@with_rw_repo('0.1.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'))
+ assert osp.isdir(osp.join(rw_repo.working_tree_dir, 'lib'))
#@skipIf(HIDE_WINDOWS_FREEZE_ERRORS, "FIXME: Freezes! sometimes...")
@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")
assert rw_remote_repo.config_reader("repository").getboolean("core", "bare")
- assert os.path.isdir(os.path.join(rw_repo.working_tree_dir, 'lib'))
+ assert osp.isdir(osp.join(rw_repo.working_tree_dir, 'lib'))
@skipIf(sys.version_info < (3,) and is_win,
"Unicode woes, see https://github.com/gitpython-developers/GitPython/pull/519")
@@ -123,7 +125,7 @@ class TestBase(TestBase):
def test_add_unicode(self, rw_repo):
filename = u"שלום.txt"
- file_path = os.path.join(rw_repo.working_dir, filename)
+ file_path = osp.join(rw_repo.working_dir, filename)
# verify first that we could encode file name in this environment
try:
diff --git a/git/test/test_commit.py b/git/test/test_commit.py
index fd9777fb..fbb1c244 100644
--- a/git/test/test_commit.py
+++ b/git/test/test_commit.py
@@ -6,34 +6,36 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
from __future__ import print_function
-from git.test.lib import (
- TestBase,
- assert_equal,
- assert_not_equal,
- with_rw_repo,
- fixture_path,
- StringProcessAdapter
-)
+from datetime import datetime
+from io import BytesIO
+import re
+import sys
+import time
+
from git import (
Commit,
Actor,
)
-from gitdb import IStream
-from git.test.lib import with_rw_directory
+from git import Repo
from git.compat import (
string_types,
text_type
)
-from git import Repo
+from git.objects.util import tzoffset, utc
from git.repo.fun import touch
+from git.test.lib import (
+ TestBase,
+ assert_equal,
+ assert_not_equal,
+ with_rw_repo,
+ fixture_path,
+ StringProcessAdapter
+)
+from git.test.lib import with_rw_directory
+from gitdb import IStream
+
+import os.path as osp
-from io import BytesIO
-import time
-import sys
-import re
-import os
-from datetime import datetime
-from git.objects.util import tzoffset, utc
try:
from unittest.mock import Mock
@@ -232,8 +234,8 @@ class TestCommit(TestBase):
@with_rw_directory
def test_ambiguous_arg_iteration(self, rw_dir):
- rw_repo = Repo.init(os.path.join(rw_dir, 'test_ambiguous_arg'))
- path = os.path.join(rw_repo.working_tree_dir, 'master')
+ rw_repo = Repo.init(osp.join(rw_dir, 'test_ambiguous_arg'))
+ path = osp.join(rw_repo.working_tree_dir, 'master')
touch(path)
rw_repo.index.add([path])
rw_repo.index.commit('initial commit')
diff --git a/git/test/test_config.py b/git/test/test_config.py
index 32873f24..0dfadda6 100644
--- a/git/test/test_config.py
+++ b/git/test/test_config.py
@@ -6,7 +6,6 @@
import glob
import io
-import os
from git import (
GitConfigParser
@@ -91,7 +90,7 @@ class TestBase(TestCase):
@with_rw_directory
def test_lock_reentry(self, rw_dir):
- fpl = os.path.join(rw_dir, 'l')
+ fpl = osp.join(rw_dir, 'l')
gcp = GitConfigParser(fpl, read_only=False)
with gcp as cw:
cw.set_value('include', 'some_value', 'a')
@@ -103,7 +102,7 @@ class TestBase(TestCase):
GitConfigParser(fpl, read_only=False)
# but work when the lock is removed
with GitConfigParser(fpl, read_only=False):
- assert os.path.exists(fpl)
+ assert osp.exists(fpl)
# reentering with an existing lock must fail due to exclusive access
with self.assertRaises(IOError):
gcp.__enter__()
@@ -178,17 +177,17 @@ class TestBase(TestCase):
# end
# PREPARE CONFIG FILE A
- fpa = os.path.join(rw_dir, 'a')
+ fpa = osp.join(rw_dir, 'a')
with GitConfigParser(fpa, read_only=False) as cw:
write_test_value(cw, 'a')
- fpb = os.path.join(rw_dir, 'b')
- fpc = os.path.join(rw_dir, 'c')
+ fpb = osp.join(rw_dir, 'b')
+ fpc = osp.join(rw_dir, 'c')
cw.set_value('include', 'relative_path_b', 'b')
cw.set_value('include', 'doesntexist', 'foobar')
cw.set_value('include', 'relative_cycle_a_a', 'a')
cw.set_value('include', 'absolute_cycle_a_a', fpa)
- assert os.path.exists(fpa)
+ assert osp.exists(fpa)
# PREPARE CONFIG FILE B
with GitConfigParser(fpb, read_only=False) as cw:
diff --git a/git/test/test_db.py b/git/test/test_db.py
index 5dcf592a..8f67dd48 100644
--- a/git/test/test_db.py
+++ b/git/test/test_db.py
@@ -3,17 +3,18 @@
#
# This module is part of GitPython and is released under
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
-from git.test.lib import TestBase
from git.db import GitCmdObjectDB
-from gitdb.util import bin_to_hex
from git.exc import BadObject
-import os
+from git.test.lib import TestBase
+from git.util import bin_to_hex
+
+import os.path as osp
class TestDB(TestBase):
def test_base(self):
- gdb = GitCmdObjectDB(os.path.join(self.rorepo.git_dir, 'objects'), self.rorepo.git)
+ gdb = GitCmdObjectDB(osp.join(self.rorepo.git_dir, 'objects'), self.rorepo.git)
# partial to complete - works with everything
hexsha = bin_to_hex(gdb.partial_to_complete_sha_hex("0.1.6"))
diff --git a/git/test/test_diff.py b/git/test/test_diff.py
index d34d84e3..48a5a641 100644
--- a/git/test/test_diff.py
+++ b/git/test/test_diff.py
@@ -4,8 +4,15 @@
#
# This module is part of GitPython and is released under
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
-import os
-
+import ddt
+from git import (
+ Repo,
+ GitCommandError,
+ Diff,
+ DiffIndex,
+ NULL_TREE,
+)
+from git.cmd import Git
from git.test.lib import (
TestBase,
StringProcessAdapter,
@@ -14,17 +21,9 @@ from git.test.lib import (
assert_true,
)
-
from git.test.lib import with_rw_directory
-from git import (
- Repo,
- GitCommandError,
- Diff,
- DiffIndex,
- NULL_TREE,
-)
-import ddt
+import os.path as osp
@ddt.ddt
@@ -53,10 +52,10 @@ class TestDiff(TestBase):
def test_diff_with_staged_file(self, rw_dir):
# SETUP INDEX WITH MULTIPLE STAGES
r = Repo.init(rw_dir)
- fp = os.path.join(rw_dir, 'hello.txt')
+ fp = osp.join(rw_dir, 'hello.txt')
with open(fp, 'w') as fs:
fs.write("hello world")
- r.git.add(fp)
+ r.git.add(Git.polish_url(fp))
r.git.commit(message="init")
with open(fp, 'w') as fs:
diff --git a/git/test/test_docs.py b/git/test/test_docs.py
index f3c75f79..bb937d93 100644
--- a/git/test/test_docs.py
+++ b/git/test/test_docs.py
@@ -9,6 +9,8 @@ import os
from git.test.lib import TestBase
from git.test.lib.helper import with_rw_directory
+import os.path as osp
+
class Tutorials(TestBase):
@@ -23,7 +25,7 @@ class Tutorials(TestBase):
def test_init_repo_object(self, rw_dir):
# [1-test_init_repo_object]
from git import Repo
- join = os.path.join
+ join = osp.join
# rorepo is a Repo instance pointing to the git-python repository.
# For all you know, the first argument to Repo is a path to the repository
@@ -62,7 +64,7 @@ class Tutorials(TestBase):
# repository paths
# [7-test_init_repo_object]
- assert os.path.isdir(cloned_repo.working_tree_dir) # directory with your work files
+ assert osp.isdir(cloned_repo.working_tree_dir) # directory with your work files
assert cloned_repo.git_dir.startswith(cloned_repo.working_tree_dir) # directory containing the git repository
assert bare_repo.working_tree_dir is None # bare repositories have no working tree
# ![7-test_init_repo_object]
@@ -146,7 +148,7 @@ class Tutorials(TestBase):
self.assertEqual(new_branch.checkout(), cloned_repo.active_branch) # checking out branch adjusts the wtree
self.assertEqual(new_branch.commit, past.commit) # Now the past is checked out
- new_file_path = os.path.join(cloned_repo.working_tree_dir, 'my-new-file')
+ new_file_path = osp.join(cloned_repo.working_tree_dir, 'my-new-file')
open(new_file_path, 'wb').close() # create new file in working tree
cloned_repo.index.add([new_file_path]) # add it to the index
# Commit the changes to deviate masters history
@@ -162,7 +164,7 @@ class Tutorials(TestBase):
# now new_branch is ahead of master, which probably should be checked out and reset softly.
# note that all these operations didn't touch the working tree, as we managed it ourselves.
# This definitely requires you to know what you are doing :) !
- assert os.path.basename(new_file_path) in new_branch.commit.tree # new file is now in tree
+ assert osp.basename(new_file_path) in new_branch.commit.tree # new file is now in tree
master.commit = new_branch.commit # let master point to most recent commit
cloned_repo.head.reference = master # we adjusted just the reference, not the working tree or index
# ![13-test_init_repo_object]
@@ -192,7 +194,7 @@ class Tutorials(TestBase):
def test_references_and_objects(self, rw_dir):
# [1-test_references_and_objects]
import git
- repo = git.Repo.clone_from(self._small_repo_url(), os.path.join(rw_dir, 'repo'), branch='master')
+ repo = git.Repo.clone_from(self._small_repo_url(), osp.join(rw_dir, 'repo'), branch='master')
heads = repo.heads
master = heads.master # lists can be accessed by name for convenience
@@ -264,7 +266,7 @@ class Tutorials(TestBase):
# [11-test_references_and_objects]
hct.blobs[0].data_stream.read() # stream object to read data from
- hct.blobs[0].stream_data(open(os.path.join(rw_dir, 'blob_data'), 'wb')) # write data to given stream
+ hct.blobs[0].stream_data(open(osp.join(rw_dir, 'blob_data'), 'wb')) # write data to given stream
# ![11-test_references_and_objects]
# [12-test_references_and_objects]
@@ -350,11 +352,11 @@ class Tutorials(TestBase):
# Access blob objects
for (path, stage), entry in index.entries.items(): # @UnusedVariable
pass
- new_file_path = os.path.join(repo.working_tree_dir, 'new-file-name')
+ new_file_path = osp.join(repo.working_tree_dir, 'new-file-name')
open(new_file_path, 'w').close()
index.add([new_file_path]) # add a new file to the index
index.remove(['LICENSE']) # remove an existing one
- assert os.path.isfile(os.path.join(repo.working_tree_dir, 'LICENSE')) # working tree is untouched
+ assert osp.isfile(osp.join(repo.working_tree_dir, 'LICENSE')) # working tree is untouched
self.assertEqual(index.commit("my commit message").type, 'commit') # commit changed index
repo.active_branch.commit = repo.commit('HEAD~1') # forget last commit
@@ -373,11 +375,11 @@ class Tutorials(TestBase):
# merge two trees three-way into memory
merge_index = IndexFile.from_tree(repo, 'HEAD~10', 'HEAD', repo.merge_base('HEAD~10', 'HEAD'))
# and persist it
- merge_index.write(os.path.join(rw_dir, 'merged_index'))
+ merge_index.write(osp.join(rw_dir, 'merged_index'))
# ![24-test_references_and_objects]
# [25-test_references_and_objects]
- empty_repo = git.Repo.init(os.path.join(rw_dir, 'empty'))
+ empty_repo = git.Repo.init(osp.join(rw_dir, 'empty'))
origin = empty_repo.create_remote('origin', repo.remotes.origin.url)
assert origin.exists()
assert origin == empty_repo.remotes.origin == empty_repo.remotes['origin']
@@ -480,8 +482,8 @@ class Tutorials(TestBase):
def test_add_file_and_commit(self, rw_dir):
import git
- repo_dir = os.path.join(rw_dir, 'my-new-repo')
- file_name = os.path.join(repo_dir, 'new-file')
+ repo_dir = osp.join(rw_dir, 'my-new-repo')
+ file_name = osp.join(repo_dir, 'new-file')
r = git.Repo.init(repo_dir)
# This function just creates an empty file ...
diff --git a/git/test/test_fun.py b/git/test/test_fun.py
index 02f338dd..9d436653 100644
--- a/git/test/test_fun.py
+++ b/git/test/test_fun.py
@@ -1,6 +1,11 @@
-from git.test.lib import (
- TestBase,
- with_rw_repo
+from io import BytesIO
+from stat import S_IFDIR, S_IFREG, S_IFLNK
+from unittest.case import skipIf
+
+from git.compat import PY3
+from git.index import IndexFile
+from git.index.fun import (
+ aggressive_tree_merge
)
from git.objects.fun import (
traverse_tree_recursive,
@@ -8,25 +13,13 @@ from git.objects.fun import (
tree_to_stream,
tree_entries_from_data
)
-
-from git.index.fun import (
- aggressive_tree_merge
+from git.test.lib import (
+ TestBase,
+ with_rw_repo
)
-
-from gitdb.util import bin_to_hex
+from git.util import bin_to_hex
from gitdb.base import IStream
from gitdb.typ import str_tree_type
-from git.compat import PY3
-
-from unittest.case import skipIf
-from stat import (
- S_IFDIR,
- S_IFREG,
- S_IFLNK
-)
-
-from git.index import IndexFile
-from io import BytesIO
class TestFun(TestBase):
@@ -262,7 +255,7 @@ class TestFun(TestBase):
def test_tree_entries_from_data_with_failing_name_decode_py2(self):
r = tree_entries_from_data(b'100644 \x9f\0aaa')
assert r == [('aaa', 33188, u'\udc9f')], r
-
+
@skipIf(not PY3, 'odd types returned ... maybe figure it out one day')
def test_tree_entries_from_data_with_failing_name_decode_py3(self):
r = tree_entries_from_data(b'100644 \x9f\0aaa')
diff --git a/git/test/test_git.py b/git/test/test_git.py
index 14c70e18..f97f8130 100644
--- a/git/test/test_git.py
+++ b/git/test/test_git.py
@@ -5,9 +5,17 @@
# This module is part of GitPython and is released under
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
import os
-import sys
import subprocess
+import sys
+from git import (
+ Git,
+ GitCommandError,
+ GitCommandNotFound,
+ Repo,
+ cmd
+)
+from git.compat import PY3, is_darwin
from git.test.lib import (
TestBase,
patch,
@@ -17,18 +25,12 @@ from git.test.lib import (
assert_match,
fixture_path
)
-from git import (
- Git,
- GitCommandError,
- GitCommandNotFound,
- Repo,
- cmd
-)
from git.test.lib import with_rw_directory
-
-from git.compat import PY3, is_darwin
from git.util import finalize_process
+import os.path as osp
+
+
try:
from unittest import mock
except ImportError:
@@ -147,7 +149,7 @@ class TestGit(TestBase):
exc = GitCommandNotFound
try:
# set it to something that doens't exist, assure it raises
- type(self.git).GIT_PYTHON_GIT_EXECUTABLE = os.path.join(
+ type(self.git).GIT_PYTHON_GIT_EXECUTABLE = osp.join(
"some", "path", "which", "doesn't", "exist", "gitbinary")
self.failUnlessRaises(exc, self.git.version)
finally:
@@ -212,13 +214,13 @@ class TestGit(TestBase):
self.assertEqual(new_env, {'VARKEY': 'VARVALUE'})
self.assertEqual(self.git.environment(), {})
- path = os.path.join(rw_dir, 'failing-script.sh')
+ path = osp.join(rw_dir, 'failing-script.sh')
with open(path, 'wt') as stream:
stream.write("#!/usr/bin/env sh\n"
"echo FOO\n")
os.chmod(path, 0o777)
- rw_repo = Repo.init(os.path.join(rw_dir, 'repo'))
+ rw_repo = Repo.init(osp.join(rw_dir, 'repo'))
remote = rw_repo.create_remote('ssh-origin', "ssh://git@server/foo")
with rw_repo.git.custom_environment(GIT_SSH=path):
diff --git a/git/test/test_index.py b/git/test/test_index.py
index d851743e..1abe22f4 100644
--- a/git/test/test_index.py
+++ b/git/test/test_index.py
@@ -26,7 +26,7 @@ from git import (
GitCommandError,
CheckoutError,
)
-from git.compat import string_types, is_win
+from git.compat import string_types, is_win, PY3
from git.exc import (
HookExecutionError,
InvalidGitRepositoryError
@@ -43,11 +43,13 @@ from git.test.lib import (
fixture,
with_rw_repo
)
-from git.util import HIDE_WINDOWS_KNOWN_ERRORS
from git.test.lib import with_rw_directory
from git.util import Actor, rmtree
+from git.util import HIDE_WINDOWS_KNOWN_ERRORS, hex_to_bin
from gitdb.base import IStream
-from gitdb.util import hex_to_bin
+
+import os.path as osp
+from git.cmd import Git
class TestIndex(TestBase):
@@ -85,7 +87,7 @@ class TestIndex(TestBase):
def _assert_entries(self, entries):
for entry in entries:
assert isinstance(entry, BaseIndexEntry)
- assert not os.path.isabs(entry.path)
+ assert not osp.isabs(entry.path)
assert "\\" not in entry.path
# END for each entry
@@ -329,7 +331,7 @@ class TestIndex(TestBase):
# reset the working copy as well to current head,to pull 'back' as well
new_data = b"will be reverted"
- file_path = os.path.join(rw_repo.working_tree_dir, "CHANGES")
+ file_path = osp.join(rw_repo.working_tree_dir, "CHANGES")
with open(file_path, "wb") as fp:
fp.write(new_data)
index.reset(rev_head_parent, working_tree=True)
@@ -340,26 +342,26 @@ class TestIndex(TestBase):
assert fp.read() != new_data
# test full checkout
- test_file = os.path.join(rw_repo.working_tree_dir, "CHANGES")
+ test_file = osp.join(rw_repo.working_tree_dir, "CHANGES")
with open(test_file, 'ab') as fd:
fd.write(b"some data")
rval = index.checkout(None, force=True, fprogress=self._fprogress)
assert 'CHANGES' in list(rval)
self._assert_fprogress([None])
- assert os.path.isfile(test_file)
+ assert osp.isfile(test_file)
os.remove(test_file)
rval = index.checkout(None, force=False, fprogress=self._fprogress)
assert 'CHANGES' in list(rval)
self._assert_fprogress([None])
- assert os.path.isfile(test_file)
+ assert osp.isfile(test_file)
# individual file
os.remove(test_file)
rval = index.checkout(test_file, fprogress=self._fprogress)
self.assertEqual(list(rval)[0], 'CHANGES')
self._assert_fprogress([test_file])
- assert os.path.exists(test_file)
+ assert osp.exists(test_file)
# checking out non-existing file throws
self.failUnlessRaises(CheckoutError, index.checkout, "doesnt_exist_ever.txt.that")
@@ -373,7 +375,7 @@ class TestIndex(TestBase):
index.checkout(test_file)
except CheckoutError as e:
self.assertEqual(len(e.failed_files), 1)
- self.assertEqual(e.failed_files[0], os.path.basename(test_file))
+ self.assertEqual(e.failed_files[0], osp.basename(test_file))
self.assertEqual(len(e.failed_files), len(e.failed_reasons))
self.assertIsInstance(e.failed_reasons[0], string_types)
self.assertEqual(len(e.valid_files), 0)
@@ -388,7 +390,7 @@ class TestIndex(TestBase):
assert not open(test_file, 'rb').read().endswith(append_data)
# checkout directory
- rmtree(os.path.join(rw_repo.working_tree_dir, "lib"))
+ rmtree(osp.join(rw_repo.working_tree_dir, "lib"))
rval = index.checkout('lib')
assert len(list(rval)) > 1
@@ -399,11 +401,17 @@ class TestIndex(TestBase):
existing = 0
basedir = repo.working_tree_dir
for f in files:
- existing += os.path.isfile(os.path.join(basedir, f))
+ existing += osp.isfile(osp.join(basedir, f))
# END for each deleted file
return existing
# END num existing helper
+ @skipIf(HIDE_WINDOWS_KNOWN_ERRORS and Git.is_cygwin(),
+ """FIXME: File "C:\projects\gitpython\git\test\test_index.py", line 642, in test_index_mutation
+ self.assertEqual(fd.read(), link_target)
+ AssertionError: '!<symlink>\xff\xfe/\x00e\x00t\x00c\x00/\x00t\x00h\x00a\x00t\x00\x00\x00'
+ != '/etc/that'
+ """)
@with_rw_repo('0.1.6')
def test_index_mutation(self, rw_repo):
index = rw_repo.index
@@ -458,7 +466,7 @@ class TestIndex(TestBase):
self.failUnlessRaises(TypeError, index.remove, [1])
# absolute path
- deleted_files = index.remove([os.path.join(rw_repo.working_tree_dir, "lib")], r=True)
+ deleted_files = index.remove([osp.join(rw_repo.working_tree_dir, "lib")], r=True)
assert len(deleted_files) > 1
self.failUnlessRaises(ValueError, index.remove, ["/doesnt/exists"])
@@ -525,9 +533,9 @@ class TestIndex(TestBase):
# re-add all files in lib
# get the lib folder back on disk, but get an index without it
index.reset(new_commit.parents[0], working_tree=True).reset(new_commit, working_tree=False)
- lib_file_path = os.path.join("lib", "git", "__init__.py")
+ lib_file_path = osp.join("lib", "git", "__init__.py")
assert (lib_file_path, 0) not in index.entries
- assert os.path.isfile(os.path.join(rw_repo.working_tree_dir, lib_file_path))
+ assert osp.isfile(osp.join(rw_repo.working_tree_dir, lib_file_path))
# directory
entries = index.add(['lib'], fprogress=self._fprogress_add)
@@ -536,14 +544,14 @@ class TestIndex(TestBase):
assert len(entries) > 1
# glob
- entries = index.reset(new_commit).add([os.path.join('lib', 'git', '*.py')], fprogress=self._fprogress_add)
+ entries = index.reset(new_commit).add([osp.join('lib', 'git', '*.py')], fprogress=self._fprogress_add)
self._assert_entries(entries)
self._assert_fprogress(entries)
self.assertEqual(len(entries), 14)
# same file
entries = index.reset(new_commit).add(
- [os.path.join(rw_repo.working_tree_dir, 'lib', 'git', 'head.py')] * 2, fprogress=self._fprogress_add)
+ [osp.join(rw_repo.working_tree_dir, 'lib', 'git', 'head.py')] * 2, fprogress=self._fprogress_add)
self._assert_entries(entries)
self.assertEqual(entries[0].mode & 0o644, 0o644)
# would fail, test is too primitive to handle this case
@@ -583,7 +591,7 @@ class TestIndex(TestBase):
for target in ('/etc/nonexisting', '/etc/passwd', '/etc'):
basename = "my_real_symlink"
- link_file = os.path.join(rw_repo.working_tree_dir, basename)
+ link_file = osp.join(rw_repo.working_tree_dir, basename)
os.symlink(target, link_file)
entries = index.reset(new_commit).add([link_file], fprogress=self._fprogress_add)
self._assert_entries(entries)
@@ -645,7 +653,7 @@ class TestIndex(TestBase):
# TEST RENAMING
def assert_mv_rval(rval):
for source, dest in rval:
- assert not os.path.exists(source) and os.path.exists(dest)
+ assert not osp.exists(source) and osp.exists(dest)
# END for each renamed item
# END move assertion utility
@@ -661,7 +669,7 @@ class TestIndex(TestBase):
paths = ['LICENSE', 'VERSION', 'doc']
rval = index.move(paths, dry_run=True)
self.assertEqual(len(rval), 2)
- assert os.path.exists(paths[0])
+ assert osp.exists(paths[0])
# again, no dry run
rval = index.move(paths)
@@ -719,8 +727,8 @@ class TestIndex(TestBase):
index.add(files, write=True)
if is_win:
hp = hook_path('pre-commit', index.repo.git_dir)
- hpd = os.path.dirname(hp)
- if not os.path.isdir(hpd):
+ 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")
@@ -766,7 +774,7 @@ class TestIndex(TestBase):
for fkey in keys:
assert fkey in index.entries
for absfile in absfiles:
- assert os.path.isfile(absfile)
+ assert osp.isfile(absfile)
@with_rw_repo('HEAD')
def test_compare_write_tree(self, rw_repo):
@@ -815,21 +823,21 @@ class TestIndex(TestBase):
# Adding using a path should still require a non-bare repository.
asserted = False
- path = os.path.join('git', 'test', 'test_index.py')
+ path = osp.join('git', 'test', 'test_index.py')
try:
rw_bare_repo.index.add([path])
except InvalidGitRepositoryError:
asserted = True
assert asserted, "Adding using a filename is not correctly asserted."
- @skipIf(HIDE_WINDOWS_KNOWN_ERRORS and sys.version_info[:2] == (2, 7), r"""
+ @skipIf(HIDE_WINDOWS_KNOWN_ERRORS and not PY3, r"""
FIXME: File "C:\projects\gitpython\git\util.py", line 125, in to_native_path_linux
return path.replace('\\', '/')
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)""")
@with_rw_directory
def test_add_utf8P_path(self, rw_dir):
# NOTE: fp is not a Unicode object in python 2 (which is the source of the problem)
- fp = os.path.join(rw_dir, 'ø.txt')
+ fp = osp.join(rw_dir, 'ø.txt')
with open(fp, 'wb') as fs:
fs.write(u'content of ø'.encode('utf-8'))
@@ -840,7 +848,7 @@ class TestIndex(TestBase):
@with_rw_directory
def test_add_a_file_with_wildcard_chars(self, rw_dir):
# see issue #407
- fp = os.path.join(rw_dir, '[.exe')
+ fp = osp.join(rw_dir, '[.exe')
with open(fp, "wb") as f:
f.write(b'something')
diff --git a/git/test/test_reflog.py b/git/test/test_reflog.py
index dffedf3b..20495be1 100644
--- a/git/test/test_reflog.py
+++ b/git/test/test_reflog.py
@@ -1,17 +1,18 @@
-from git.test.lib import (
- TestBase,
- fixture_path
-)
+import os
+import tempfile
+
from git.objects import IndexObject
from git.refs import (
RefLogEntry,
RefLog
)
-from git.util import Actor, rmtree
-from gitdb.util import hex_to_bin
+from git.test.lib import (
+ TestBase,
+ fixture_path
+)
+from git.util import Actor, rmtree, hex_to_bin
-import tempfile
-import os
+import os.path as osp
class TestRefLog(TestBase):
@@ -42,7 +43,7 @@ class TestRefLog(TestBase):
os.mkdir(tdir)
rlp_master_ro = RefLog.path(self.rorepo.head)
- assert os.path.isfile(rlp_master_ro)
+ assert osp.isfile(rlp_master_ro)
# simple read
reflog = RefLog.from_file(rlp_master_ro)
@@ -70,7 +71,7 @@ class TestRefLog(TestBase):
cr = self.rorepo.config_reader()
for rlp in (rlp_head, rlp_master):
reflog = RefLog.from_file(rlp)
- tfile = os.path.join(tdir, os.path.basename(rlp))
+ tfile = osp.join(tdir, osp.basename(rlp))
reflog.to_file(tfile)
assert reflog.write() is reflog
diff --git a/git/test/test_refs.py b/git/test/test_refs.py
index 43f1dcc7..fd0be108 100644
--- a/git/test/test_refs.py
+++ b/git/test/test_refs.py
@@ -4,10 +4,8 @@
# This module is part of GitPython and is released under
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
-from git.test.lib import (
- TestBase,
- with_rw_repo
-)
+from itertools import chain
+
from git import (
Reference,
Head,
@@ -18,11 +16,15 @@ from git import (
GitCommandError,
RefLog
)
-import git.refs as refs
-from git.util import Actor
from git.objects.tag import TagObject
-from itertools import chain
-import os
+from git.test.lib import (
+ TestBase,
+ with_rw_repo
+)
+from git.util import Actor
+
+import git.refs as refs
+import os.path as osp
class TestRefs(TestBase):
@@ -268,10 +270,10 @@ class TestRefs(TestBase):
assert tmp_head == new_head and tmp_head.object == new_head.object
logfile = RefLog.path(tmp_head)
- assert os.path.isfile(logfile)
+ assert osp.isfile(logfile)
Head.delete(rw_repo, tmp_head)
# deletion removes the log as well
- assert not os.path.isfile(logfile)
+ assert not osp.isfile(logfile)
heads = rw_repo.heads
assert tmp_head not in heads and new_head not in heads
# force on deletion testing would be missing here, code looks okay though ;)
@@ -450,12 +452,12 @@ class TestRefs(TestBase):
symbol_ref_path = "refs/symbol_ref"
symref = SymbolicReference(rw_repo, symbol_ref_path)
assert symref.path == symbol_ref_path
- symbol_ref_abspath = os.path.join(rw_repo.git_dir, symref.path)
+ symbol_ref_abspath = osp.join(rw_repo.git_dir, symref.path)
# set it
symref.reference = new_head
assert symref.reference == new_head
- assert os.path.isfile(symbol_ref_abspath)
+ assert osp.isfile(symbol_ref_abspath)
assert symref.commit == new_head.commit
for name in ('absname', 'folder/rela_name'):
@@ -507,7 +509,7 @@ class TestRefs(TestBase):
rw_repo.head.reference = Head.create(rw_repo, "master")
# At least the head should still exist
- assert os.path.isfile(os.path.join(rw_repo.git_dir, 'HEAD'))
+ assert osp.isfile(osp.join(rw_repo.git_dir, 'HEAD'))
refs = list(SymbolicReference.iter_items(rw_repo))
assert len(refs) == 1
diff --git a/git/test/test_repo.py b/git/test/test_repo.py
index a0a6a5b0..8b644f7f 100644
--- a/git/test/test_repo.py
+++ b/git/test/test_repo.py
@@ -50,10 +50,9 @@ from git.test.lib import (
assert_true,
raises
)
-from git.util import HIDE_WINDOWS_KNOWN_ERRORS
+from git.util import HIDE_WINDOWS_KNOWN_ERRORS, cygpath
from git.test.lib import with_rw_directory
-from git.util import join_path_native, rmtree, rmfile
-from gitdb.util import bin_to_hex
+from git.util import join_path_native, rmtree, rmfile, bin_to_hex
from unittest import SkipTest
import functools as fnt
@@ -203,8 +202,8 @@ class TestRepo(TestBase):
prev_cwd = os.getcwd()
os.chdir(tempfile.gettempdir())
git_dir_rela = "repos/foo/bar.git"
- del_dir_abs = os.path.abspath("repos")
- git_dir_abs = os.path.abspath(git_dir_rela)
+ del_dir_abs = osp.abspath("repos")
+ git_dir_abs = osp.abspath(git_dir_rela)
try:
# with specific path
for path in (git_dir_rela, git_dir_abs):
@@ -212,7 +211,7 @@ class TestRepo(TestBase):
self.assertIsInstance(r, Repo)
assert r.bare is True
assert not r.has_separate_working_tree()
- assert os.path.isdir(r.git_dir)
+ assert osp.isdir(r.git_dir)
self._assert_empty_repo(r)
@@ -306,16 +305,16 @@ class TestRepo(TestBase):
def test_is_dirty_with_path(self, rwrepo):
assert rwrepo.is_dirty(path="git") is False
- with open(os.path.join(rwrepo.working_dir, "git", "util.py"), "at") as f:
+ with open(osp.join(rwrepo.working_dir, "git", "util.py"), "at") as f:
f.write("junk")
assert rwrepo.is_dirty(path="git") is True
assert rwrepo.is_dirty(path="doc") is False
- rwrepo.git.add(os.path.join("git", "util.py"))
+ rwrepo.git.add(Git.polish_url(osp.join("git", "util.py")))
assert rwrepo.is_dirty(index=False, path="git") is False
assert rwrepo.is_dirty(path="git") is True
- with open(os.path.join(rwrepo.working_dir, "doc", "no-such-file.txt"), "wt") as f:
+ with open(osp.join(rwrepo.working_dir, "doc", "no-such-file.txt"), "wt") as f:
f.write("junk")
assert rwrepo.is_dirty(path="doc") is False
assert rwrepo.is_dirty(untracked_files=True, path="doc") is True
@@ -412,6 +411,14 @@ class TestRepo(TestBase):
self.assertEqual(len(res), 1)
self.assertEqual(len(res[0][1]), 83, "Unexpected amount of parsed blame lines")
+ @skipIf(HIDE_WINDOWS_KNOWN_ERRORS and Git.is_cygwin(),
+ """FIXME: File "C:\projects\gitpython\git\cmd.py", line 671, in execute
+ raise GitCommandError(command, status, stderr_value, stdout_value)
+ GitCommandError: Cmd('git') failed due to: exit code(128)
+ cmdline: git add 1__��ava verb��ten 1_test _myfile 1_test_other_file
+ 1_��ava-----verb��ten
+ stderr: 'fatal: pathspec '"1__çava verböten"' did not match any files'
+ """)
@with_rw_repo('HEAD', bare=False)
def test_untracked_files(self, rwrepo):
for run, (repo_add, is_invoking_git) in enumerate((
@@ -494,10 +501,10 @@ class TestRepo(TestBase):
ph = os.environ.get('HOME')
try:
os.environ['HOME'] = rw_dir
- Repo.init(os.path.join('~', 'test.git'), bare=True)
+ Repo.init(osp.join('~', 'test.git'), bare=True)
os.environ['FOO'] = rw_dir
- Repo.init(os.path.join('$FOO', 'test.git'), bare=True)
+ Repo.init(osp.join('$FOO', 'test.git'), bare=True)
finally:
if ph:
os.environ['HOME'] = ph
@@ -785,7 +792,7 @@ class TestRepo(TestBase):
@with_rw_repo('HEAD')
def test_git_file(self, rwrepo):
# Move the .git directory to another location and create the .git file.
- real_path_abs = os.path.abspath(join_path_native(rwrepo.working_tree_dir, '.real'))
+ real_path_abs = osp.abspath(join_path_native(rwrepo.working_tree_dir, '.real'))
os.rename(rwrepo.git_dir, real_path_abs)
git_file_path = join_path_native(rwrepo.working_tree_dir, '.git')
with open(git_file_path, 'wb') as fp:
@@ -793,13 +800,13 @@ class TestRepo(TestBase):
# Create a repo and make sure it's pointing to the relocated .git directory.
git_file_repo = Repo(rwrepo.working_tree_dir)
- self.assertEqual(os.path.abspath(git_file_repo.git_dir), real_path_abs)
+ self.assertEqual(osp.abspath(git_file_repo.git_dir), real_path_abs)
# Test using an absolute gitdir path in the .git file.
with open(git_file_path, 'wb') as fp:
fp.write(('gitdir: %s\n' % real_path_abs).encode('ascii'))
git_file_repo = Repo(rwrepo.working_tree_dir)
- self.assertEqual(os.path.abspath(git_file_repo.git_dir), real_path_abs)
+ self.assertEqual(osp.abspath(git_file_repo.git_dir), real_path_abs)
@skipIf(HIDE_WINDOWS_KNOWN_ERRORS and PY3,
"FIXME: smmp fails with: TypeError: Can't convert 'bytes' object to str implicitly")
@@ -840,7 +847,7 @@ class TestRepo(TestBase):
# It's expected to not be able to access a tree
self.failUnlessRaises(ValueError, r.tree)
- new_file_path = os.path.join(rw_dir, "new_file.ext")
+ new_file_path = osp.join(rw_dir, "new_file.ext")
touch(new_file_path)
r.index.add([new_file_path])
r.index.commit("initial commit\nBAD MESSAGE 1\n")
@@ -901,9 +908,6 @@ class TestRepo(TestBase):
for i, j in itertools.permutations([c1, 'ffffff', ''], r=2):
self.assertRaises(GitCommandError, repo.is_ancestor, i, j)
- # @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") # noqa E501
@with_rw_directory
def test_work_tree_unsupported(self, rw_dir):
git = Git(rw_dir)
@@ -913,6 +917,8 @@ class TestRepo(TestBase):
rw_master = self.rorepo.clone(join_path_native(rw_dir, 'master_repo'))
rw_master.git.checkout('HEAD~10')
worktree_path = join_path_native(rw_dir, 'worktree_repo')
+ if Git.is_cygwin():
+ worktree_path = cygpath(worktree_path)
try:
rw_master.git.worktree('add', worktree_path, 'master')
except Exception as ex:
diff --git a/git/test/test_submodule.py b/git/test/test_submodule.py
index 9db4f9c9..fcaad04b 100644
--- a/git/test/test_submodule.py
+++ b/git/test/test_submodule.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# This module is part of GitPython and is released under
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
import os
@@ -6,24 +7,34 @@ from unittest.case import skipIf
import git
from git.cmd import Git
-from git.compat import string_types, is_win
+from git.compat import (
+ string_types,
+ is_win,
+)
from git.exc import (
InvalidGitRepositoryError,
RepositoryDirtyError
)
from git.objects.submodule.base import Submodule
-from git.objects.submodule.root import RootModule, RootUpdateProgress
+from git.objects.submodule.root import (
+ RootModule,
+ RootUpdateProgress,
+)
from git.repo.fun import (
find_git_dir,
- touch
+ touch,
)
from git.test.lib import (
TestBase,
- with_rw_repo
+ with_rw_repo,
)
from git.test.lib import with_rw_directory
-from git.util import HIDE_WINDOWS_KNOWN_ERRORS
-from git.util import to_native_path_linux, join_path_native
+from git.util import (
+ to_native_path_linux,
+ join_path_native,
+ HIDE_WINDOWS_KNOWN_ERRORS,
+)
+
import os.path as osp
@@ -673,6 +684,13 @@ class TestSubmodule(TestBase):
url=empty_repo_dir, no_checkout=checkout_mode and True or False)
# end for each checkout mode
+ @skipIf(HIDE_WINDOWS_KNOWN_ERRORS and Git.is_cygwin(),
+ """FIXME: ile "C:\projects\gitpython\git\cmd.py", line 671, in execute
+ raise GitCommandError(command, status, stderr_value, stdout_value)
+ GitCommandError: Cmd('git') failed due to: exit code(128)
+ cmdline: git add 1__Xava verbXXten 1_test _myfile 1_test_other_file 1_XXava-----verbXXten
+ stderr: 'fatal: pathspec '"1__çava verböten"' did not match any files'
+ """)
@with_rw_directory
def test_git_submodules_and_add_sm_with_new_commit(self, rwdir):
parent = git.Repo.init(osp.join(rwdir, 'parent'))
@@ -705,7 +723,7 @@ class TestSubmodule(TestBase):
fp = osp.join(smm.working_tree_dir, 'empty-file')
with open(fp, 'w'):
pass
- smm.git.add(fp)
+ smm.git.add(Git.polish_url(fp))
smm.git.commit(m="new file added")
# submodules are retrieved from the current commit's tree, therefore we can't really get a new submodule
@@ -730,7 +748,7 @@ class TestSubmodule(TestBase):
assert commit_sm.binsha == sm_too.binsha
assert sm_too.binsha != sm.binsha
- # @skipIf(HIDE_WINDOWS_KNOWN_ERRORS,
+ # @skipIf(HIDE_WINDOWS_KNOWN_ERRORS, ## ACTUALLY skipped by `git.submodule.base#L869`.
# "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") # noqa E501
@with_rw_directory
diff --git a/git/test/test_tree.py b/git/test/test_tree.py
index f36c4337..f9259874 100644
--- a/git/test/test_tree.py
+++ b/git/test/test_tree.py
@@ -5,7 +5,6 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
from io import BytesIO
-import os
import sys
from unittest.case import skipIf
@@ -13,8 +12,10 @@ from git import (
Tree,
Blob
)
-from git.util import HIDE_WINDOWS_KNOWN_ERRORS
from git.test.lib import TestBase
+from git.util import HIDE_WINDOWS_KNOWN_ERRORS
+
+import os.path as osp
class TestTree(TestBase):
@@ -90,12 +91,12 @@ class TestTree(TestBase):
assert len(set(b for b in root if isinstance(b, Blob)) | set(root.blobs)) == len(root.blobs)
subitem = trees[0][0]
assert "/" in subitem.path
- assert subitem.name == os.path.basename(subitem.path)
+ assert subitem.name == osp.basename(subitem.path)
# assure that at some point the traversed paths have a slash in them
found_slash = False
for item in root.traverse():
- assert os.path.isabs(item.abspath)
+ assert osp.isabs(item.abspath)
if '/' in item.path:
found_slash = True
# END check for slash
diff --git a/git/test/test_util.py b/git/test/test_util.py
index e07417b4..8f8d2272 100644
--- a/git/test/test_util.py
+++ b/git/test/test_util.py
@@ -5,7 +5,19 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
import tempfile
+import time
+from unittest.case import skipIf
+
+import ddt
+from git.cmd import dashify
+from git.compat import string_types, is_win
+from git.objects.util import (
+ altz_to_utctz_str,
+ utctz_to_altz,
+ verify_utctz,
+ parse_date,
+)
from git.test.lib import (
TestBase,
assert_equal
@@ -15,19 +27,37 @@ from git.util import (
BlockingLockFile,
get_user_id,
Actor,
- IterableList
+ IterableList,
+ cygpath,
+ decygpath
)
-from git.objects.util import (
- altz_to_utctz_str,
- utctz_to_altz,
- verify_utctz,
- parse_date,
+
+
+_norm_cygpath_pairs = (
+ (r'foo\bar', 'foo/bar'),
+ (r'foo/bar', 'foo/bar'),
+
+ (r'C:\Users', '/cygdrive/c/Users'),
+ (r'C:\d/e', '/cygdrive/c/d/e'),
+
+ ('C:\\', '/cygdrive/c/'),
+
+ (r'\\server\C$\Users', '//server/C$/Users'),
+ (r'\\server\C$', '//server/C$'),
+ ('\\\\server\\c$\\', '//server/c$/'),
+ (r'\\server\BAR/', '//server/BAR/'),
+
+ (r'D:/Apps', '/cygdrive/d/Apps'),
+ (r'D:/Apps\fOO', '/cygdrive/d/Apps/fOO'),
+ (r'D:\Apps/123', '/cygdrive/d/Apps/123'),
)
-from git.cmd import dashify
-from git.compat import string_types, is_win
-import time
-import ddt
+_unc_cygpath_pairs = (
+ (r'\\?\a:\com', '/cygdrive/a/com'),
+ (r'\\?\a:/com', '/cygdrive/a/com'),
+
+ (r'\\?\UNC\server\D$\Apps', '//server/D$/Apps'),
+)
class TestIterableMember(object):
@@ -52,6 +82,46 @@ class TestUtils(TestBase):
"array": [42],
}
+ @skipIf(not is_win, "Paths specifically for Windows.")
+ @ddt.idata(_norm_cygpath_pairs + _unc_cygpath_pairs)
+ def test_cygpath_ok(self, case):
+ wpath, cpath = case
+ cwpath = cygpath(wpath)
+ self.assertEqual(cwpath, cpath, wpath)
+
+ @skipIf(not is_win, "Paths specifically for Windows.")
+ @ddt.data(
+ (r'./bar', 'bar'),
+ (r'.\bar', 'bar'),
+ (r'../bar', '../bar'),
+ (r'..\bar', '../bar'),
+ (r'../bar/.\foo/../chu', '../bar/chu'),
+ )
+ def test_cygpath_norm_ok(self, case):
+ wpath, cpath = case
+ cwpath = cygpath(wpath)
+ self.assertEqual(cwpath, cpath or wpath, wpath)
+
+ @skipIf(not is_win, "Paths specifically for Windows.")
+ @ddt.data(
+ r'C:',
+ r'C:Relative',
+ r'D:Apps\123',
+ r'D:Apps/123',
+ r'\\?\a:rel',
+ r'\\share\a:rel',
+ )
+ def test_cygpath_invalids(self, wpath):
+ cwpath = cygpath(wpath)
+ self.assertEqual(cwpath, wpath.replace('\\', '/'), wpath)
+
+ @skipIf(not is_win, "Paths specifically for Windows.")
+ @ddt.idata(_norm_cygpath_pairs)
+ def test_decygpath(self, case):
+ wpath, cpath = case
+ wcpath = decygpath(cpath)
+ self.assertEqual(wcpath, wpath.replace('/', '\\'), cpath)
+
def test_it_should_dashify(self):
assert_equal('this-is-my-argument', dashify('this_is_my_argument'))
assert_equal('foo', dashify('foo'))
diff --git a/git/util.py b/git/util.py
index 3dd12d39..1e0d3eb4 100644
--- a/git/util.py
+++ b/git/util.py
@@ -5,6 +5,8 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
from __future__ import unicode_literals
+import contextlib
+from functools import wraps
import getpass
import logging
import os
@@ -13,19 +15,21 @@ import re
import shutil
import stat
import time
+from unittest.case import SkipTest
-from functools import wraps
-
-from git.compat import is_win
from gitdb.util import (# NOQA @IgnorePep8
make_sha,
LockedFD, # @UnusedImport
file_contents_ro, # @UnusedImport
+ file_contents_ro_filepath, # @UnusedImport
LazyMixin, # @UnusedImport
to_hex_sha, # @UnusedImport
- to_bin_sha # @UnusedImport
+ to_bin_sha, # @UnusedImport
+ bin_to_hex, # @UnusedImport
+ hex_to_bin, # @UnusedImport
)
+from git.compat import is_win
import os.path as osp
from .compat import (
@@ -34,7 +38,6 @@ from .compat import (
PY3
)
from .exc import InvalidGitRepositoryError
-from unittest.case import SkipTest
# NOTE: Some of the unused imports might be used/imported by others.
@@ -47,11 +50,13 @@ __all__ = ("stream_copy", "join_path", "to_native_path_windows", "to_native_path
'RemoteProgress', 'CallableRemoteProgress', 'rmtree', 'unbare_repo',
'HIDE_WINDOWS_KNOWN_ERRORS')
+log = logging.getLogger(__name__)
+
#: We need an easy way to see if Appveyor TCs start failing,
#: so the errors marked with this var are considered "acknowledged" ones, awaiting remedy,
#: till then, we wish to hide them.
HIDE_WINDOWS_KNOWN_ERRORS = is_win and os.environ.get('HIDE_WINDOWS_KNOWN_ERRORS', True)
-HIDE_WINDOWS_FREEZE_ERRORS = is_win and os.environ.get('HIDE_WINDOWS_FREEZE_ERRORS', HIDE_WINDOWS_KNOWN_ERRORS)
+HIDE_WINDOWS_FREEZE_ERRORS = is_win and os.environ.get('HIDE_WINDOWS_FREEZE_ERRORS', True)
#{ Utility Methods
@@ -70,6 +75,16 @@ def unbare_repo(func):
return wrapper
+@contextlib.contextmanager
+def cwd(new_dir):
+ old_dir = os.getcwd()
+ os.chdir(new_dir)
+ try:
+ yield new_dir
+ finally:
+ os.chdir(old_dir)
+
+
def rmtree(path):
"""Remove the given recursively.
@@ -116,7 +131,7 @@ def stream_copy(source, destination, chunk_size=512 * 1024):
def join_path(a, *p):
- """Join path tokens together similar to os.path.join, but always use
+ """Join path tokens together similar to osp.join, but always use
'/' instead of possibly '\' on windows."""
path = a
for b in p:
@@ -162,14 +177,154 @@ def assure_directory_exists(path, is_file=False):
Otherwise it must be a directory
:return: True if the directory was created, False if it already existed"""
if is_file:
- path = os.path.dirname(path)
+ path = osp.dirname(path)
# END handle file
- if not os.path.isdir(path):
+ if not osp.isdir(path):
os.makedirs(path)
return True
return False
+def _get_exe_extensions():
+ try:
+ winprog_exts = tuple(p.upper() for p in os.environ['PATHEXT'].split(os.pathsep))
+ except:
+ winprog_exts = ('.BAT', 'COM', '.EXE')
+
+ return winprog_exts
+
+
+def py_where(program, path=None):
+ # From: http://stackoverflow.com/a/377028/548792
+ try:
+ winprog_exts = tuple(p.upper() for p in os.environ['PATHEXT'].split(os.pathsep))
+ except:
+ winprog_exts = is_win and ('.BAT', 'COM', '.EXE') or ()
+
+ def is_exec(fpath):
+ return osp.isfile(fpath) and os.access(fpath, os.X_OK) and (
+ os.name != 'nt' or not winprog_exts or any(fpath.upper().endswith(ext)
+ for ext in winprog_exts))
+
+ progs = []
+ if not path:
+ path = os.environ["PATH"]
+ for folder in path.split(os.pathsep):
+ folder = folder.strip('"')
+ if folder:
+ exe_path = osp.join(folder, program)
+ for f in [exe_path] + ['%s%s' % (exe_path, e) for e in winprog_exts]:
+ if is_exec(f):
+ progs.append(f)
+ return progs
+
+
+def _cygexpath(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)
+ p = path
+ else:
+ p = path and osp.normpath(osp.expandvars(osp.expanduser(path)))
+ if osp.isabs(p):
+ if drive:
+ # Confusing, maybe a remote system should expand vars.
+ p = path
+ else:
+ p = cygpath(p)
+ elif drive:
+ p = '/cygdrive/%s/%s' % (drive.lower(), p)
+
+ return p.replace('\\', '/')
+
+
+_cygpath_parsers = (
+ ## See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
+ ## and: https://www.cygwin.com/cygwin-ug-net/using.html#unc-paths
+ (re.compile(r"\\\\\?\\UNC\\([^\\]+)\\([^\\]+)(?:\\(.*))?"),
+ (lambda server, share, rest_path: '//%s/%s/%s' % (server, share, rest_path.replace('\\', '/'))),
+ False
+ ),
+
+ (re.compile(r"\\\\\?\\(\w):[/\\](.*)"),
+ _cygexpath,
+ False
+ ),
+
+ (re.compile(r"(\w):[/\\](.*)"),
+ _cygexpath,
+ False
+ ),
+
+ (re.compile(r"file:(.*)", re.I),
+ (lambda rest_path: rest_path),
+ True),
+
+ (re.compile(r"(\w{2,}:.*)"), # remote URL, do nothing
+ (lambda url: url),
+ False),
+)
+
+
+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)
+
+ return path
+
+
+_decygpath_regex = re.compile(r"/cygdrive/(\w)(/.*)?")
+
+
+def decygpath(path):
+ m = _decygpath_regex.match(path)
+ if m:
+ drive, rest_path = m.groups()
+ path = '%s:%s' % (drive.upper(), rest_path or '')
+
+ return path.replace('/', '\\')
+
+
+#: Store boolean flags denoting if a specific Git executable
+#: is from a Cygwin installation (since `cache_lru()` unsupported on PY2).
+_is_cygwin_cache = {}
+
+
+def is_cygwin_git(git_executable):
+ if not is_win:
+ return False
+
+ from subprocess import check_output
+
+ is_cygwin = _is_cygwin_cache.get(git_executable)
+ if is_cygwin is None:
+ is_cygwin = False
+ try:
+ git_dir = osp.dirname(git_executable)
+ if not git_dir:
+ res = py_where(git_executable)
+ git_dir = osp.dirname(res[0]) if res else None
+
+ ## Just a name given, not a real path.
+ uname_cmd = osp.join(git_dir, 'uname')
+ uname = check_output(uname_cmd, universal_newlines=True)
+ is_cygwin = 'CYGWIN' in uname
+ except Exception as ex:
+ log.debug('Failed checking if running in CYGWIN due to: %r', ex)
+ _is_cygwin_cache[git_executable] = is_cygwin
+
+ return is_cygwin
+
+
def get_user_id():
""":return: string identifying the currently active system user as name@node"""
return "%s@%s" % (getpass.getuser(), platform.node())
@@ -589,7 +744,7 @@ class LockFile(object):
if self._has_lock():
return
lock_file = self._lock_file_path()
- if os.path.isfile(lock_file):
+ if osp.isfile(lock_file):
raise IOError("Lock for file %r did already exist, delete %r in case the lock is illegal" %
(self._file_path, lock_file))
@@ -659,7 +814,7 @@ class BlockingLockFile(LockFile):
# synity check: if the directory leading to the lockfile is not
# readable anymore, raise an exception
curtime = time.time()
- if not os.path.isdir(os.path.dirname(self._lock_file_path())):
+ if not osp.isdir(osp.dirname(self._lock_file_path())):
msg = "Directory containing the lockfile %r was not readable anymore after waiting %g seconds" % (
self._lock_file_path(), curtime - starttime)
raise IOError(msg)
diff --git a/requirements.txt b/requirements.txt
index 85d25511..39644606 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,2 @@
gitdb>=0.6.4
-ddt
-mock \ No newline at end of file
+ddt>=1.1.1
diff --git a/setup.py b/setup.py
index 737e52a6..cbf5cf57 100755
--- a/setup.py
+++ b/setup.py
@@ -68,7 +68,7 @@ install_requires = ['gitdb2 >= 2.0.0']
extras_require = {
':python_version == "2.6"': ['ordereddict'],
}
-test_requires = ['ddt']
+test_requires = ['ddt>=1.1.1']
if sys.version_info[:2] < (2, 7):
test_requires.append('mock')
diff --git a/test-requirements.txt b/test-requirements.txt
index 4d08a501..8f13395d 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -3,4 +3,4 @@
coverage
flake8
nose
-mock
+mock; python_version<='2.7'