diff options
author | Sebastian Thiel <byronimo@gmail.com> | 2017-09-28 15:17:25 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-28 15:17:25 +0200 |
commit | 58998fb2dd6a1cad5faffdc36ae536ee6b04e3d2 (patch) | |
tree | 4f5c342b1c20b23a5bc73eb74855c4bb81f2c197 | |
parent | 251128d41cdf39a49468ed5d997cc1640339ccbc (diff) | |
parent | 2af601d5800a39ab04e9fe6cf22ef7b917ab5d67 (diff) | |
download | gitpython-58998fb2dd6a1cad5faffdc36ae536ee6b04e3d2.tar.gz |
Merge branch 'master' into master
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | git/__init__.py | 37 | ||||
-rw-r--r-- | git/cmd.py | 4 | ||||
m--------- | git/ext/gitdb | 0 | ||||
-rw-r--r-- | git/objects/submodule/base.py | 10 | ||||
-rw-r--r-- | git/refs/remote.py | 4 | ||||
-rw-r--r-- | git/refs/symbolic.py | 44 | ||||
-rw-r--r-- | git/remote.py | 7 | ||||
-rw-r--r-- | git/repo/base.py | 50 | ||||
-rw-r--r-- | git/test/test_docs.py | 4 | ||||
-rw-r--r-- | git/test/test_repo.py | 2 | ||||
-rw-r--r-- | git/util.py | 10 |
14 files changed, 108 insertions, 69 deletions
@@ -20,5 +20,6 @@ Contributors are: -Konstantin Popov <konstantin.popov.89 _at_ yandex.ru> -Peter Jones <pjones _at_ redhat.com> -Anson Mansfield <anson.mansfield _at_ gmail.com> +-Alexis Horgix Chotard Portions derived from other open source works and are clearly marked. @@ -14,5 +14,5 @@ release: clean force_release: clean git push --tags - python setup.py sdist bdist_wheel + python3 setup.py sdist bdist_wheel twine upload -s -i byronimo@gmail.com dist/* @@ -1 +1 @@ -2.1.5 +2.1.6 diff --git a/git/__init__.py b/git/__init__.py index 8c31e309..75247dbf 100644 --- a/git/__init__.py +++ b/git/__init__.py @@ -35,23 +35,26 @@ _init_externals() #{ Imports -from git.config import GitConfigParser # @NoMove @IgnorePep8 -from git.objects import * # @NoMove @IgnorePep8 -from git.refs import * # @NoMove @IgnorePep8 -from git.diff import * # @NoMove @IgnorePep8 -from git.exc import * # @NoMove @IgnorePep8 -from git.db import * # @NoMove @IgnorePep8 -from git.cmd import Git # @NoMove @IgnorePep8 -from git.repo import Repo # @NoMove @IgnorePep8 -from git.remote import * # @NoMove @IgnorePep8 -from git.index import * # @NoMove @IgnorePep8 -from git.util import ( # @NoMove @IgnorePep8 - LockFile, - BlockingLockFile, - Stats, - Actor, - rmtree, -) +from git.exc import * # @NoMove @IgnorePep8 +try: + from git.config import GitConfigParser # @NoMove @IgnorePep8 + from git.objects import * # @NoMove @IgnorePep8 + from git.refs import * # @NoMove @IgnorePep8 + from git.diff import * # @NoMove @IgnorePep8 + from git.db import * # @NoMove @IgnorePep8 + from git.cmd import Git # @NoMove @IgnorePep8 + from git.repo import Repo # @NoMove @IgnorePep8 + from git.remote import * # @NoMove @IgnorePep8 + from git.index import * # @NoMove @IgnorePep8 + from git.util import ( # @NoMove @IgnorePep8 + LockFile, + BlockingLockFile, + Stats, + Actor, + rmtree, + ) +except GitError as exc: + raise ImportError('%s: %s' % (exc.__class__.__name__, exc)) #} END imports @@ -31,7 +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 git.util import is_cygwin_git, cygpath, expand_path from .exc import ( GitCommandError, @@ -405,7 +405,7 @@ class Git(LazyMixin): It is meant to be the working tree directory if available, or the .git directory in case of bare repositories.""" super(Git, self).__init__() - self._working_dir = working_dir + self._working_dir = expand_path(working_dir) self._git_options = () self._persistent_git_options = [] diff --git a/git/ext/gitdb b/git/ext/gitdb -Subproject 38866bc7c4956170c681a62c4508f934ac82646 +Subproject c0fd43b5ff8c356fcf9cdebbbbd1803a502b465 diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index e3912d88..a6b4caed 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -123,12 +123,12 @@ class Submodule(IndexObject, Iterable, Traversable): reader = self.config_reader() # default submodule values try: - self.path = reader.get_value('path') + self.path = reader.get('path') except cp.NoSectionError: raise ValueError("This submodule instance does not exist anymore in '%s' file" % osp.join(self.repo.working_tree_dir, '.gitmodules')) # end - self._url = reader.get_value('url') + self._url = reader.get('url') # git-python extension values - optional self._branch_path = reader.get_value(self.k_head_option, git.Head.to_full_path(self.k_head_default)) elif attr == '_name': @@ -1168,11 +1168,11 @@ class Submodule(IndexObject, Iterable, Traversable): for sms in parser.sections(): n = sm_name(sms) - p = parser.get_value(sms, 'path') - u = parser.get_value(sms, 'url') + p = parser.get(sms, 'path') + u = parser.get(sms, 'url') b = cls.k_head_default if parser.has_option(sms, cls.k_head_option): - b = str(parser.get_value(sms, cls.k_head_option)) + b = str(parser.get(sms, cls.k_head_option)) # END handle optional information # get the binsha diff --git a/git/refs/remote.py b/git/refs/remote.py index ef69b5db..0164e110 100644 --- a/git/refs/remote.py +++ b/git/refs/remote.py @@ -37,6 +37,10 @@ class RemoteReference(Head): # and delete remainders manually for ref in refs: try: + os.remove(osp.join(repo.common_dir, ref.path)) + except OSError: + pass + try: os.remove(osp.join(repo.git_dir, ref.path)) except OSError: pass diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py index 90ecb62c..bef6ba3c 100644 --- a/git/refs/symbolic.py +++ b/git/refs/symbolic.py @@ -26,6 +26,14 @@ from .log import RefLog __all__ = ["SymbolicReference"] +def _git_dir(repo, path): + """ Find the git dir that's appropriate for the path""" + name = "%s" % (path,) + if name in ['HEAD', 'ORIG_HEAD', 'FETCH_HEAD', 'index', 'logs']: + return repo.git_dir + return repo.common_dir + + class SymbolicReference(object): """Represents a special case of a reference such that this reference is symbolic. @@ -71,16 +79,11 @@ class SymbolicReference(object): @property def abspath(self): - return join_path_native(self.repo.git_dir, self.path) + return join_path_native(_git_dir(self.repo, self.path), self.path) @classmethod def _get_packed_refs_path(cls, repo): - try: - commondir = open(osp.join(repo.git_dir, 'commondir'), 'rt').readlines()[0].strip() - except (OSError, IOError): - commondir = '.' - repodir = osp.join(repo.git_dir, commondir) - return osp.join(repodir, 'packed-refs') + return osp.join(repo.common_dir, 'packed-refs') @classmethod def _iter_packed_refs(cls, repo): @@ -127,11 +130,12 @@ class SymbolicReference(object): # END recursive dereferencing @classmethod - def _get_ref_info_helper(cls, repo, repodir, ref_path): + def _get_ref_info_helper(cls, repo, ref_path): """Return: (str(sha), str(target_ref_path)) if available, the sha the file at rela_path points to, or None. target_ref_path is the reference we point to, or None""" tokens = None + repodir = _git_dir(repo, ref_path) try: with open(osp.join(repodir, ref_path), 'rt') as fp: value = fp.read().rstrip() @@ -169,16 +173,7 @@ class SymbolicReference(object): """Return: (str(sha), str(target_ref_path)) if available, the sha the file at rela_path points to, or None. target_ref_path is the reference we point to, or None""" - try: - return cls._get_ref_info_helper(repo, repo.git_dir, ref_path) - except ValueError: - try: - commondir = open(osp.join(repo.git_dir, 'commondir'), 'rt').readlines()[0].strip() - except (OSError, IOError): - commondir = '.' - - repodir = osp.join(repo.git_dir, commondir) - return cls._get_ref_info_helper(repo, repodir, ref_path) + return cls._get_ref_info_helper(repo, ref_path) def _get_object(self): """ @@ -433,7 +428,7 @@ 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 = osp.join(repo.git_dir, full_ref_path) + abs_path = osp.join(repo.common_dir, full_ref_path) if osp.exists(abs_path): os.remove(abs_path) else: @@ -484,8 +479,9 @@ class SymbolicReference(object): a proper symbolic reference. Otherwise it will be resolved to the corresponding object and a detached symbolic reference will be created instead""" + git_dir = _git_dir(repo, path) full_ref_path = cls.to_full_path(path) - abs_ref_path = osp.join(repo.git_dir, full_ref_path) + abs_ref_path = osp.join(git_dir, full_ref_path) # figure out target data target = reference @@ -559,8 +555,8 @@ class SymbolicReference(object): if self.path == new_path: return self - new_abs_path = osp.join(self.repo.git_dir, new_path) - cur_abs_path = osp.join(self.repo.git_dir, self.path) + new_abs_path = osp.join(_git_dir(self.repo, new_path), new_path) + cur_abs_path = osp.join(_git_dir(self.repo, self.path), self.path) if osp.isfile(new_abs_path): if not force: # if they point to the same file, its not an error @@ -594,7 +590,7 @@ class SymbolicReference(object): # walk loose refs # Currently we do not follow links - for root, dirs, files in os.walk(join_path_native(repo.git_dir, common_path)): + for root, dirs, files in os.walk(join_path_native(repo.common_dir, common_path)): if 'refs' not in root.split(os.sep): # skip non-refs subfolders refs_id = [d for d in dirs if d == 'refs'] if refs_id: @@ -605,7 +601,7 @@ class SymbolicReference(object): if f == 'packed-refs': continue abs_path = to_native_path_linux(join_path(root, f)) - rela_paths.add(abs_path.replace(to_native_path_linux(repo.git_dir) + '/', "")) + rela_paths.add(abs_path.replace(to_native_path_linux(repo.common_dir) + '/', "")) # END for each file in root directory # END for each directory to walk diff --git a/git/remote.py b/git/remote.py index fd76e592..81719f4b 100644 --- a/git/remote.py +++ b/git/remote.py @@ -38,6 +38,7 @@ from .refs import ( log = logging.getLogger('git.remote') +log.addHandler(logging.NullHandler()) __all__ = ('RemoteProgress', 'PushInfo', 'FetchInfo', 'Remote') @@ -208,7 +209,7 @@ class FetchInfo(object): NEW_TAG, NEW_HEAD, HEAD_UPTODATE, TAG_UPDATE, REJECTED, FORCED_UPDATE, \ FAST_FORWARD, ERROR = [1 << x for x in range(8)] - re_fetch_result = re.compile(r'^\s*(.) (\[?[\w\s\.$@]+\]?)\s+(.+) -> ([^\s]+)( \(.*\)?$)?') + _re_fetch_result = re.compile(r'^\s*(.) (\[?[\w\s\.$@]+\]?)\s+(.+) -> ([^\s]+)( \(.*\)?$)?') _flag_map = {'!': ERROR, '+': FORCED_UPDATE, @@ -263,7 +264,7 @@ class FetchInfo(object): fetch line is the corresponding line from FETCH_HEAD, like acb0fa8b94ef421ad60c8507b634759a472cd56c not-for-merge branch '0.1.7RC' of /tmp/tmpya0vairemote_repo""" - match = cls.re_fetch_result.match(line) + match = cls._re_fetch_result.match(line) if match is None: raise ValueError("Failed to parse line: %r" % line) @@ -652,7 +653,7 @@ class Remote(LazyMixin, Iterable): continue # read head information - with open(osp.join(self.repo.git_dir, 'FETCH_HEAD'), 'rb') as fp: + with open(osp.join(self.repo.common_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 28bb2a5d..d3bdc983 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -9,6 +9,7 @@ import logging import os import re import sys +import warnings from git.cmd import ( Git, @@ -29,7 +30,7 @@ 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 +from git.util import Actor, finalize_process, decygpath, hex_to_bin, expand_path import os.path as osp from .fun import rev_parse, is_git_dir, find_submodule_git_dir, touch, find_worktree_git_dir @@ -50,10 +51,6 @@ BlameEntry = namedtuple('BlameEntry', ['commit', 'linenos', 'orig_path', 'orig_l __all__ = ('Repo',) -def _expand_path(p): - return osp.normpath(osp.abspath(osp.expandvars(osp.expanduser(p)))) - - class Repo(object): """Represents a git repository and allows you to query references, gather commit information, generate diffs, create and clone repositories query @@ -74,6 +71,7 @@ class Repo(object): working_dir = None _working_tree_dir = None git_dir = None + _common_dir = None # precompiled regex re_whitespace = re.compile(r'\s+') @@ -90,7 +88,7 @@ class Repo(object): # Subclasses may easily bring in their own custom types by placing a constructor or type here GitCommandWrapperType = Git - def __init__(self, path=None, odbt=DefaultDBType, search_parent_directories=False): + def __init__(self, path=None, odbt=DefaultDBType, search_parent_directories=False, expand_vars=True): """Create a new Repo instance :param path: @@ -116,12 +114,18 @@ class Repo(object): :raise InvalidGitRepositoryError: :raise NoSuchPathError: :return: git.Repo """ + epath = path or os.getenv('GIT_DIR') if not epath: epath = os.getcwd() if Git.is_cygwin(): epath = decygpath(epath) - epath = _expand_path(epath or path or os.getcwd()) + + epath = epath or path or os.getcwd() + if expand_vars and ("%" in epath or "$" in epath): + warnings.warn("The use of environment variables in paths is deprecated" + + "\nfor security reasons and may be removed in the future!!") + epath = expand_path(epath, expand_vars) if not os.path.exists(epath): raise NoSuchPathError(epath) @@ -148,7 +152,7 @@ class Repo(object): sm_gitpath = find_worktree_git_dir(dotgit) if sm_gitpath is not None: - self.git_dir = _expand_path(sm_gitpath) + self.git_dir = expand_path(sm_gitpath, expand_vars) self._working_tree_dir = curpath break @@ -169,17 +173,23 @@ class Repo(object): # lets not assume the option exists, although it should pass + try: + common_dir = open(osp.join(self.git_dir, 'commondir'), 'rt').readlines()[0].strip() + self._common_dir = osp.join(self.git_dir, common_dir) + except (OSError, IOError): + self._common_dir = None + # adjust the wd in case we are actually bare - we didn't know that # in the first place if self._bare: self._working_tree_dir = None # END working dir handling - self.working_dir = self._working_tree_dir or self.git_dir + self.working_dir = self._working_tree_dir or self.common_dir self.git = self.GitCommandWrapperType(self.working_dir) # special handling, in special times - args = [osp.join(self.git_dir, 'objects')] + args = [osp.join(self.common_dir, 'objects')] if issubclass(odbt, GitCmdObjectDB): args.append(self.git) self.odb = odbt(*args) @@ -237,6 +247,13 @@ class Repo(object): return self._working_tree_dir @property + def common_dir(self): + """:return: The git dir that holds everything except possibly HEAD, + FETCH_HEAD, ORIG_HEAD, COMMIT_EDITMSG, index, and logs/ . + """ + return self._common_dir or self.git_dir + + @property def bare(self): """:return: True if the repository is bare""" return self._bare @@ -574,7 +591,7 @@ class Repo(object): :note: The method does not check for the existence of the paths in alts as the caller is responsible.""" - alternates_path = osp.join(self.git_dir, 'objects', 'info', 'alternates') + alternates_path = osp.join(self.common_dir, 'objects', 'info', 'alternates') if not alts: if osp.isfile(alternates_path): os.remove(alternates_path) @@ -844,7 +861,7 @@ class Repo(object): return blames @classmethod - def init(cls, path=None, mkdir=True, odbt=DefaultDBType, **kwargs): + def init(cls, path=None, mkdir=True, odbt=DefaultDBType, expand_vars=True, **kwargs): """Initialize a git repository at the given path if specified :param path: @@ -862,12 +879,17 @@ class Repo(object): the directory containing the database objects, i.e. .git/objects. It will be used to access all object data + :param expand_vars: + if specified, environment variables will not be escaped. This + can lead to information disclosure, allowing attackers to + access the contents of environment variables + :parm kwargs: keyword arguments serving as additional options to the git-init command :return: ``git.Repo`` (the newly created repo)""" if path: - path = _expand_path(path) + path = expand_path(path, expand_vars) if mkdir and path and not osp.exists(path): os.makedirs(path, 0o755) @@ -932,7 +954,7 @@ class Repo(object): * All remaining keyword arguments are given to the git-clone command :return: ``git.Repo`` (the newly cloned repo)""" - return self._clone(self.git, self.git_dir, path, type(self.odb), progress, **kwargs) + return self._clone(self.git, self.common_dir, path, type(self.odb), progress, **kwargs) @classmethod def clone_from(cls, url, to_path, progress=None, env=None, **kwargs): diff --git a/git/test/test_docs.py b/git/test/test_docs.py index cbbd9447..1ba3f482 100644 --- a/git/test/test_docs.py +++ b/git/test/test_docs.py @@ -289,9 +289,9 @@ class Tutorials(TestBase): assert len(headcommit.hexsha) == 40 assert len(headcommit.parents) > 0 assert headcommit.tree.type == 'tree' - assert headcommit.author.name == 'Sebastian Thiel' + assert len(headcommit.author.name) != 0 assert isinstance(headcommit.authored_date, int) - assert headcommit.committer.name == 'Sebastian Thiel' + assert len(headcommit.committer.name) != 0 assert isinstance(headcommit.committed_date, int) assert headcommit.message != '' # ![14-test_references_and_objects] diff --git a/git/test/test_repo.py b/git/test/test_repo.py index a6be4e66..312e67f9 100644 --- a/git/test/test_repo.py +++ b/git/test/test_repo.py @@ -935,6 +935,8 @@ class TestRepo(TestBase): commit = repo.head.commit self.assertIsInstance(commit, Object) + self.assertIsInstance(repo.heads['aaaaaaaa'], Head) + @with_rw_directory def test_git_work_tree_env(self, rw_dir): """Check that we yield to GIT_WORK_TREE""" diff --git a/git/util.py b/git/util.py index 5553a0aa..5baeee91 100644 --- a/git/util.py +++ b/git/util.py @@ -340,6 +340,16 @@ def finalize_process(proc, **kwargs): ## TODO: No close proc-streams?? proc.wait(**kwargs) + +def expand_path(p, expand_vars=True): + try: + p = osp.expanduser(p) + if expand_vars: + p = osp.expandvars(p) + return osp.normpath(osp.abspath(p)) + except: + return None + #} END utilities #{ Classes |