Skip to content

Commit a56136f

Browse files
authoredSep 28, 2017
Merge branch 'master' into adding_setup_for_git_executable
2 parents 90dc03d + 2af601d commit a56136f

File tree

14 files changed

+109
-70
lines changed

14 files changed

+109
-70
lines changed
 

‎AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ Contributors are:
2020
-Konstantin Popov <konstantin.popov.89 _at_ yandex.ru>
2121
-Peter Jones <pjones _at_ redhat.com>
2222
-Ken Odegard <ken.odegard _at_ gmail.com>
23+
-Alexis Horgix Chotard
2324

2425
Portions derived from other open source works and are clearly marked.

‎Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ release: clean
1414

1515
force_release: clean
1616
git push --tags
17-
python setup.py sdist bdist_wheel
17+
python3 setup.py sdist bdist_wheel
1818
twine upload -s -i byronimo@gmail.com dist/*

‎VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.1.5
1+
2.1.6

‎git/__init__.py

+20-17
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,26 @@ def _init_externals():
3535

3636
#{ Imports
3737

38-
from git.config import GitConfigParser # @NoMove @IgnorePep8
39-
from git.objects import * # @NoMove @IgnorePep8
40-
from git.refs import * # @NoMove @IgnorePep8
41-
from git.diff import * # @NoMove @IgnorePep8
42-
from git.exc import * # @NoMove @IgnorePep8
43-
from git.db import * # @NoMove @IgnorePep8
44-
from git.cmd import Git # @NoMove @IgnorePep8
45-
from git.repo import Repo # @NoMove @IgnorePep8
46-
from git.remote import * # @NoMove @IgnorePep8
47-
from git.index import * # @NoMove @IgnorePep8
48-
from git.util import ( # @NoMove @IgnorePep8
49-
LockFile,
50-
BlockingLockFile,
51-
Stats,
52-
Actor,
53-
rmtree,
54-
)
38+
from git.exc import * # @NoMove @IgnorePep8
39+
try:
40+
from git.config import GitConfigParser # @NoMove @IgnorePep8
41+
from git.objects import * # @NoMove @IgnorePep8
42+
from git.refs import * # @NoMove @IgnorePep8
43+
from git.diff import * # @NoMove @IgnorePep8
44+
from git.db import * # @NoMove @IgnorePep8
45+
from git.cmd import Git # @NoMove @IgnorePep8
46+
from git.repo import Repo # @NoMove @IgnorePep8
47+
from git.remote import * # @NoMove @IgnorePep8
48+
from git.index import * # @NoMove @IgnorePep8
49+
from git.util import ( # @NoMove @IgnorePep8
50+
LockFile,
51+
BlockingLockFile,
52+
Stats,
53+
Actor,
54+
rmtree,
55+
)
56+
except GitError as exc:
57+
raise ImportError('%s: %s' % (exc.__class__.__name__, exc))
5558

5659
#} END imports
5760

‎git/cmd.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
)
3333
from git.exc import CommandError
3434
from git.odict import OrderedDict
35-
from git.util import is_cygwin_git, cygpath
35+
from git.util import is_cygwin_git, cygpath, expand_path
3636

3737
from .exc import (
3838
GitCommandError,
@@ -531,7 +531,7 @@ def __init__(self, working_dir=None):
531531
It is meant to be the working tree directory if available, or the
532532
.git directory in case of bare repositories."""
533533
super(Git, self).__init__()
534-
self._working_dir = working_dir
534+
self._working_dir = expand_path(working_dir)
535535
self._git_options = ()
536536
self._persistent_git_options = []
537537

‎git/objects/submodule/base.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,12 @@ def _set_cache_(self, attr):
123123
reader = self.config_reader()
124124
# default submodule values
125125
try:
126-
self.path = reader.get_value('path')
126+
self.path = reader.get('path')
127127
except cp.NoSectionError:
128128
raise ValueError("This submodule instance does not exist anymore in '%s' file"
129129
% osp.join(self.repo.working_tree_dir, '.gitmodules'))
130130
# end
131-
self._url = reader.get_value('url')
131+
self._url = reader.get('url')
132132
# git-python extension values - optional
133133
self._branch_path = reader.get_value(self.k_head_option, git.Head.to_full_path(self.k_head_default))
134134
elif attr == '_name':
@@ -1168,11 +1168,11 @@ def iter_items(cls, repo, parent_commit='HEAD'):
11681168

11691169
for sms in parser.sections():
11701170
n = sm_name(sms)
1171-
p = parser.get_value(sms, 'path')
1172-
u = parser.get_value(sms, 'url')
1171+
p = parser.get(sms, 'path')
1172+
u = parser.get(sms, 'url')
11731173
b = cls.k_head_default
11741174
if parser.has_option(sms, cls.k_head_option):
1175-
b = str(parser.get_value(sms, cls.k_head_option))
1175+
b = str(parser.get(sms, cls.k_head_option))
11761176
# END handle optional information
11771177

11781178
# get the binsha

‎git/refs/remote.py

+4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ def delete(cls, repo, *refs, **kwargs):
3636
# are generally ignored in the refs/ folder. We don't though
3737
# and delete remainders manually
3838
for ref in refs:
39+
try:
40+
os.remove(osp.join(repo.common_dir, ref.path))
41+
except OSError:
42+
pass
3943
try:
4044
os.remove(osp.join(repo.git_dir, ref.path))
4145
except OSError:

‎git/refs/symbolic.py

+20-24
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@
2626
__all__ = ["SymbolicReference"]
2727

2828

29+
def _git_dir(repo, path):
30+
""" Find the git dir that's appropriate for the path"""
31+
name = "%s" % (path,)
32+
if name in ['HEAD', 'ORIG_HEAD', 'FETCH_HEAD', 'index', 'logs']:
33+
return repo.git_dir
34+
return repo.common_dir
35+
36+
2937
class SymbolicReference(object):
3038

3139
"""Represents a special case of a reference such that this reference is symbolic.
@@ -71,16 +79,11 @@ def name(self):
7179

7280
@property
7381
def abspath(self):
74-
return join_path_native(self.repo.git_dir, self.path)
82+
return join_path_native(_git_dir(self.repo, self.path), self.path)
7583

7684
@classmethod
7785
def _get_packed_refs_path(cls, repo):
78-
try:
79-
commondir = open(osp.join(repo.git_dir, 'commondir'), 'rt').readlines()[0].strip()
80-
except (OSError, IOError):
81-
commondir = '.'
82-
repodir = osp.join(repo.git_dir, commondir)
83-
return osp.join(repodir, 'packed-refs')
86+
return osp.join(repo.common_dir, 'packed-refs')
8487

8588
@classmethod
8689
def _iter_packed_refs(cls, repo):
@@ -127,11 +130,12 @@ def dereference_recursive(cls, repo, ref_path):
127130
# END recursive dereferencing
128131

129132
@classmethod
130-
def _get_ref_info_helper(cls, repo, repodir, ref_path):
133+
def _get_ref_info_helper(cls, repo, ref_path):
131134
"""Return: (str(sha), str(target_ref_path)) if available, the sha the file at
132135
rela_path points to, or None. target_ref_path is the reference we
133136
point to, or None"""
134137
tokens = None
138+
repodir = _git_dir(repo, ref_path)
135139
try:
136140
with open(osp.join(repodir, ref_path), 'rt') as fp:
137141
value = fp.read().rstrip()
@@ -169,16 +173,7 @@ def _get_ref_info(cls, repo, ref_path):
169173
"""Return: (str(sha), str(target_ref_path)) if available, the sha the file at
170174
rela_path points to, or None. target_ref_path is the reference we
171175
point to, or None"""
172-
try:
173-
return cls._get_ref_info_helper(repo, repo.git_dir, ref_path)
174-
except ValueError:
175-
try:
176-
commondir = open(osp.join(repo.git_dir, 'commondir'), 'rt').readlines()[0].strip()
177-
except (OSError, IOError):
178-
commondir = '.'
179-
180-
repodir = osp.join(repo.git_dir, commondir)
181-
return cls._get_ref_info_helper(repo, repodir, ref_path)
176+
return cls._get_ref_info_helper(repo, ref_path)
182177

183178
def _get_object(self):
184179
"""
@@ -433,7 +428,7 @@ def delete(cls, repo, path):
433428
or just "myreference", hence 'refs/' is implied.
434429
Alternatively the symbolic reference to be deleted"""
435430
full_ref_path = cls.to_full_path(path)
436-
abs_path = osp.join(repo.git_dir, full_ref_path)
431+
abs_path = osp.join(repo.common_dir, full_ref_path)
437432
if osp.exists(abs_path):
438433
os.remove(abs_path)
439434
else:
@@ -484,8 +479,9 @@ def _create(cls, repo, path, resolve, reference, force, logmsg=None):
484479
a proper symbolic reference. Otherwise it will be resolved to the
485480
corresponding object and a detached symbolic reference will be created
486481
instead"""
482+
git_dir = _git_dir(repo, path)
487483
full_ref_path = cls.to_full_path(path)
488-
abs_ref_path = osp.join(repo.git_dir, full_ref_path)
484+
abs_ref_path = osp.join(git_dir, full_ref_path)
489485

490486
# figure out target data
491487
target = reference
@@ -559,8 +555,8 @@ def rename(self, new_path, force=False):
559555
if self.path == new_path:
560556
return self
561557

562-
new_abs_path = osp.join(self.repo.git_dir, new_path)
563-
cur_abs_path = osp.join(self.repo.git_dir, self.path)
558+
new_abs_path = osp.join(_git_dir(self.repo, new_path), new_path)
559+
cur_abs_path = osp.join(_git_dir(self.repo, self.path), self.path)
564560
if osp.isfile(new_abs_path):
565561
if not force:
566562
# if they point to the same file, its not an error
@@ -594,7 +590,7 @@ def _iter_items(cls, repo, common_path=None):
594590

595591
# walk loose refs
596592
# Currently we do not follow links
597-
for root, dirs, files in os.walk(join_path_native(repo.git_dir, common_path)):
593+
for root, dirs, files in os.walk(join_path_native(repo.common_dir, common_path)):
598594
if 'refs' not in root.split(os.sep): # skip non-refs subfolders
599595
refs_id = [d for d in dirs if d == 'refs']
600596
if refs_id:
@@ -605,7 +601,7 @@ def _iter_items(cls, repo, common_path=None):
605601
if f == 'packed-refs':
606602
continue
607603
abs_path = to_native_path_linux(join_path(root, f))
608-
rela_paths.add(abs_path.replace(to_native_path_linux(repo.git_dir) + '/', ""))
604+
rela_paths.add(abs_path.replace(to_native_path_linux(repo.common_dir) + '/', ""))
609605
# END for each file in root directory
610606
# END for each directory to walk
611607

‎git/remote.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939

4040
log = logging.getLogger('git.remote')
41+
log.addHandler(logging.NullHandler())
4142

4243

4344
__all__ = ('RemoteProgress', 'PushInfo', 'FetchInfo', 'Remote')
@@ -208,7 +209,7 @@ class FetchInfo(object):
208209
NEW_TAG, NEW_HEAD, HEAD_UPTODATE, TAG_UPDATE, REJECTED, FORCED_UPDATE, \
209210
FAST_FORWARD, ERROR = [1 << x for x in range(8)]
210211

211-
re_fetch_result = re.compile(r'^\s*(.) (\[?[\w\s\.$@]+\]?)\s+(.+) -> ([^\s]+)( \(.*\)?$)?')
212+
_re_fetch_result = re.compile(r'^\s*(.) (\[?[\w\s\.$@]+\]?)\s+(.+) -> ([^\s]+)( \(.*\)?$)?')
212213

213214
_flag_map = {
214215
'!': ERROR,
@@ -284,7 +285,7 @@ def _from_line(cls, repo, line, fetch_line):
284285
285286
fetch line is the corresponding line from FETCH_HEAD, like
286287
acb0fa8b94ef421ad60c8507b634759a472cd56c not-for-merge branch '0.1.7RC' of /tmp/tmpya0vairemote_repo"""
287-
match = cls.re_fetch_result.match(line)
288+
match = cls._re_fetch_result.match(line)
288289
if match is None:
289290
raise ValueError("Failed to parse line: %r" % line)
290291

@@ -673,7 +674,7 @@ def _get_fetch_info_from_stderr(self, proc, progress):
673674
continue
674675

675676
# read head information
676-
with open(osp.join(self.repo.git_dir, 'FETCH_HEAD'), 'rb') as fp:
677+
with open(osp.join(self.repo.common_dir, 'FETCH_HEAD'), 'rb') as fp:
677678
fetch_head_info = [l.decode(defenc) for l in fp.readlines()]
678679

679680
l_fil = len(fetch_info_lines)

‎git/repo/base.py

+36-14
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import os
1010
import re
1111
import sys
12+
import warnings
1213

1314
from git.cmd import (
1415
Git,
@@ -29,7 +30,7 @@
2930
from git.objects import Submodule, RootModule, Commit
3031
from git.refs import HEAD, Head, Reference, TagReference
3132
from git.remote import Remote, add_progress, to_progress_instance
32-
from git.util import Actor, finalize_process, decygpath, hex_to_bin
33+
from git.util import Actor, finalize_process, decygpath, hex_to_bin, expand_path
3334
import os.path as osp
3435

3536
from .fun import rev_parse, is_git_dir, find_submodule_git_dir, touch, find_worktree_git_dir
@@ -50,10 +51,6 @@
5051
__all__ = ('Repo',)
5152

5253

53-
def _expand_path(p):
54-
return osp.normpath(osp.abspath(osp.expandvars(osp.expanduser(p))))
55-
56-
5754
class Repo(object):
5855
"""Represents a git repository and allows you to query references,
5956
gather commit information, generate diffs, create and clone repositories query
@@ -74,6 +71,7 @@ class Repo(object):
7471
working_dir = None
7572
_working_tree_dir = None
7673
git_dir = None
74+
_common_dir = None
7775

7876
# precompiled regex
7977
re_whitespace = re.compile(r'\s+')
@@ -90,7 +88,7 @@ class Repo(object):
9088
# Subclasses may easily bring in their own custom types by placing a constructor or type here
9189
GitCommandWrapperType = Git
9290

93-
def __init__(self, path=None, odbt=DefaultDBType, search_parent_directories=False):
91+
def __init__(self, path=None, odbt=DefaultDBType, search_parent_directories=False, expand_vars=True):
9492
"""Create a new Repo instance
9593
9694
:param path:
@@ -116,12 +114,18 @@ def __init__(self, path=None, odbt=DefaultDBType, search_parent_directories=Fals
116114
:raise InvalidGitRepositoryError:
117115
:raise NoSuchPathError:
118116
:return: git.Repo """
117+
119118
epath = path or os.getenv('GIT_DIR')
120119
if not epath:
121120
epath = os.getcwd()
122121
if Git.is_cygwin():
123122
epath = decygpath(epath)
124-
epath = _expand_path(epath or path or os.getcwd())
123+
124+
epath = epath or path or os.getcwd()
125+
if expand_vars and ("%" in epath or "$" in epath):
126+
warnings.warn("The use of environment variables in paths is deprecated" +
127+
"\nfor security reasons and may be removed in the future!!")
128+
epath = expand_path(epath, expand_vars)
125129
if not os.path.exists(epath):
126130
raise NoSuchPathError(epath)
127131

@@ -148,7 +152,7 @@ def __init__(self, path=None, odbt=DefaultDBType, search_parent_directories=Fals
148152
sm_gitpath = find_worktree_git_dir(dotgit)
149153

150154
if sm_gitpath is not None:
151-
self.git_dir = _expand_path(sm_gitpath)
155+
self.git_dir = expand_path(sm_gitpath, expand_vars)
152156
self._working_tree_dir = curpath
153157
break
154158

@@ -169,17 +173,23 @@ def __init__(self, path=None, odbt=DefaultDBType, search_parent_directories=Fals
169173
# lets not assume the option exists, although it should
170174
pass
171175

176+
try:
177+
common_dir = open(osp.join(self.git_dir, 'commondir'), 'rt').readlines()[0].strip()
178+
self._common_dir = osp.join(self.git_dir, common_dir)
179+
except (OSError, IOError):
180+
self._common_dir = None
181+
172182
# adjust the wd in case we are actually bare - we didn't know that
173183
# in the first place
174184
if self._bare:
175185
self._working_tree_dir = None
176186
# END working dir handling
177187

178-
self.working_dir = self._working_tree_dir or self.git_dir
188+
self.working_dir = self._working_tree_dir or self.common_dir
179189
self.git = self.GitCommandWrapperType(self.working_dir)
180190

181191
# special handling, in special times
182-
args = [osp.join(self.git_dir, 'objects')]
192+
args = [osp.join(self.common_dir, 'objects')]
183193
if issubclass(odbt, GitCmdObjectDB):
184194
args.append(self.git)
185195
self.odb = odbt(*args)
@@ -236,6 +246,13 @@ def working_tree_dir(self):
236246
"""
237247
return self._working_tree_dir
238248

249+
@property
250+
def common_dir(self):
251+
""":return: The git dir that holds everything except possibly HEAD,
252+
FETCH_HEAD, ORIG_HEAD, COMMIT_EDITMSG, index, and logs/ .
253+
"""
254+
return self._common_dir or self.git_dir
255+
239256
@property
240257
def bare(self):
241258
""":return: True if the repository is bare"""
@@ -574,7 +591,7 @@ def _set_alternates(self, alts):
574591
:note:
575592
The method does not check for the existence of the paths in alts
576593
as the caller is responsible."""
577-
alternates_path = osp.join(self.git_dir, 'objects', 'info', 'alternates')
594+
alternates_path = osp.join(self.common_dir, 'objects', 'info', 'alternates')
578595
if not alts:
579596
if osp.isfile(alternates_path):
580597
os.remove(alternates_path)
@@ -844,7 +861,7 @@ def blame(self, rev, file, incremental=False, **kwargs):
844861
return blames
845862

846863
@classmethod
847-
def init(cls, path=None, mkdir=True, odbt=DefaultDBType, **kwargs):
864+
def init(cls, path=None, mkdir=True, odbt=DefaultDBType, expand_vars=True, **kwargs):
848865
"""Initialize a git repository at the given path if specified
849866
850867
:param path:
@@ -862,12 +879,17 @@ def init(cls, path=None, mkdir=True, odbt=DefaultDBType, **kwargs):
862879
the directory containing the database objects, i.e. .git/objects.
863880
It will be used to access all object data
864881
882+
:param expand_vars:
883+
if specified, environment variables will not be escaped. This
884+
can lead to information disclosure, allowing attackers to
885+
access the contents of environment variables
886+
865887
:parm kwargs:
866888
keyword arguments serving as additional options to the git-init command
867889
868890
:return: ``git.Repo`` (the newly created repo)"""
869891
if path:
870-
path = _expand_path(path)
892+
path = expand_path(path, expand_vars)
871893
if mkdir and path and not osp.exists(path):
872894
os.makedirs(path, 0o755)
873895

@@ -932,7 +954,7 @@ def clone(self, path, progress=None, **kwargs):
932954
* All remaining keyword arguments are given to the git-clone command
933955
934956
:return: ``git.Repo`` (the newly cloned repo)"""
935-
return self._clone(self.git, self.git_dir, path, type(self.odb), progress, **kwargs)
957+
return self._clone(self.git, self.common_dir, path, type(self.odb), progress, **kwargs)
936958

937959
@classmethod
938960
def clone_from(cls, url, to_path, progress=None, env=None, **kwargs):

‎git/test/test_docs.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -289,9 +289,9 @@ def test_references_and_objects(self, rw_dir):
289289
assert len(headcommit.hexsha) == 40
290290
assert len(headcommit.parents) > 0
291291
assert headcommit.tree.type == 'tree'
292-
assert headcommit.author.name == 'Sebastian Thiel'
292+
assert len(headcommit.author.name) != 0
293293
assert isinstance(headcommit.authored_date, int)
294-
assert headcommit.committer.name == 'Sebastian Thiel'
294+
assert len(headcommit.committer.name) != 0
295295
assert isinstance(headcommit.committed_date, int)
296296
assert headcommit.message != ''
297297
# ![14-test_references_and_objects]

‎git/test/test_repo.py

+2
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,8 @@ def test_git_work_tree_dotgit(self, rw_dir):
935935
commit = repo.head.commit
936936
self.assertIsInstance(commit, Object)
937937

938+
self.assertIsInstance(repo.heads['aaaaaaaa'], Head)
939+
938940
@with_rw_directory
939941
def test_git_work_tree_env(self, rw_dir):
940942
"""Check that we yield to GIT_WORK_TREE"""

‎git/util.py

+10
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,16 @@ def finalize_process(proc, **kwargs):
340340
## TODO: No close proc-streams??
341341
proc.wait(**kwargs)
342342

343+
344+
def expand_path(p, expand_vars=True):
345+
try:
346+
p = osp.expanduser(p)
347+
if expand_vars:
348+
p = osp.expandvars(p)
349+
return osp.normpath(osp.abspath(p))
350+
except:
351+
return None
352+
343353
#} END utilities
344354

345355
#{ Classes

0 commit comments

Comments
 (0)
Please sign in to comment.