summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Thiel <byronimo@gmail.com>2017-09-28 15:17:25 +0200
committerGitHub <noreply@github.com>2017-09-28 15:17:25 +0200
commit58998fb2dd6a1cad5faffdc36ae536ee6b04e3d2 (patch)
tree4f5c342b1c20b23a5bc73eb74855c4bb81f2c197
parent251128d41cdf39a49468ed5d997cc1640339ccbc (diff)
parent2af601d5800a39ab04e9fe6cf22ef7b917ab5d67 (diff)
downloadgitpython-58998fb2dd6a1cad5faffdc36ae536ee6b04e3d2.tar.gz
Merge branch 'master' into master
-rw-r--r--AUTHORS1
-rw-r--r--Makefile2
-rw-r--r--VERSION2
-rw-r--r--git/__init__.py37
-rw-r--r--git/cmd.py4
m---------git/ext/gitdb0
-rw-r--r--git/objects/submodule/base.py10
-rw-r--r--git/refs/remote.py4
-rw-r--r--git/refs/symbolic.py44
-rw-r--r--git/remote.py7
-rw-r--r--git/repo/base.py50
-rw-r--r--git/test/test_docs.py4
-rw-r--r--git/test/test_repo.py2
-rw-r--r--git/util.py10
14 files changed, 108 insertions, 69 deletions
diff --git a/AUTHORS b/AUTHORS
index a54f45d2..69fecb52 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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.
diff --git a/Makefile b/Makefile
index 4c72721c..c6ce5a9c 100644
--- a/Makefile
+++ b/Makefile
@@ -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/*
diff --git a/VERSION b/VERSION
index cd57a8b9..399088bf 100644
--- a/VERSION
+++ b/VERSION
@@ -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
diff --git a/git/cmd.py b/git/cmd.py
index 6022180a..67143e0e 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -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