Skip to content

Commit b8b025f

Browse files
committed
Win, #519: FIX repo TCs.
+ FIX TestRepo.test_submodule_update(): + submod: del `.git` file prior overwrite; Windows denied otherwise! + FIX TestRepo.test_untracked_files(): + In the `git add <file>` case, it failed with unicode args on PY2. Had to encode them with `locale.getpreferredencoding()` AND use SHELL. + cmd: add `shell` into `execute()` kwds, for overriding USE_SHELL per command. + repo: replace blocky `communicate()` in `_clone()` with thread-pumps. + test_repo.py: unittestize (almost all) assertions. + Replace open --> with open for index (base and TC). + test_index.py: Enabled a dormant assertion.
1 parent a79cf67 commit b8b025f

File tree

8 files changed

+177
-143
lines changed

8 files changed

+177
-143
lines changed

Diff for: git/cmd.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
execute_kwargs = set(('istream', 'with_keep_cwd', 'with_extended_output',
4646
'with_exceptions', 'as_process', 'stdout_as_string',
4747
'output_stream', 'with_stdout', 'kill_after_timeout',
48-
'universal_newlines'))
48+
'universal_newlines', 'shell'))
4949

5050
log = logging.getLogger('git.cmd')
5151
log.addHandler(logging.NullHandler())
@@ -176,8 +176,8 @@ def __setstate__(self, d):
176176
GIT_PYTHON_GIT_EXECUTABLE = os.environ.get(_git_exec_env_var, git_exec_name)
177177

178178
# If True, a shell will be used when executing git commands.
179-
# This should only be desirable on windows, see https://github.com/gitpython-developers/GitPython/pull/126
180-
# for more information
179+
# This should only be desirable on Windows, see https://github.com/gitpython-developers/GitPython/pull/126
180+
# and check `git/test_repo.py:TestRepo.test_untracked_files()` TC for an example where it is required.
181181
# Override this value using `Git.USE_SHELL = True`
182182
USE_SHELL = False
183183

@@ -422,6 +422,7 @@ def execute(self, command,
422422
kill_after_timeout=None,
423423
with_stdout=True,
424424
universal_newlines=False,
425+
shell=None,
425426
**subprocess_kwargs
426427
):
427428
"""Handles executing the command on the shell and consumes and returns
@@ -479,6 +480,9 @@ def execute(self, command,
479480
:param universal_newlines:
480481
if True, pipes will be opened as text, and lines are split at
481482
all known line endings.
483+
:param shell:
484+
Whether to invoke commands through a shell (see `Popen(..., shell=True)`).
485+
It overrides :attr:`USE_SHELL` if it is not `None`.
482486
:param kill_after_timeout:
483487
To specify a timeout in seconds for the git command, after which the process
484488
should be killed. This will have no effect if as_process is set to True. It is
@@ -544,7 +548,7 @@ def execute(self, command,
544548
stdin=istream,
545549
stderr=PIPE,
546550
stdout=PIPE if with_stdout else open(os.devnull, 'wb'),
547-
shell=self.USE_SHELL,
551+
shell=shell is not None and shell or self.USE_SHELL,
548552
close_fds=(is_posix), # unsupported on windows
549553
universal_newlines=universal_newlines,
550554
creationflags=PROC_CREATIONFLAGS,

Diff for: git/compat.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"""utilities to help provide compatibility with python 3"""
88
# flake8: noqa
99

10+
import locale
1011
import os
1112
import sys
1213

@@ -15,14 +16,14 @@
1516
MAXSIZE,
1617
izip,
1718
)
18-
1919
from gitdb.utils.encoding import (
2020
string_types,
2121
text_type,
2222
force_bytes,
2323
force_text
2424
)
2525

26+
2627
PY3 = sys.version_info[0] >= 3
2728
is_win = (os.name == 'nt')
2829
is_posix = (os.name == 'posix')
@@ -76,6 +77,16 @@ def safe_encode(s):
7677
raise TypeError('Expected bytes or text, but got %r' % (s,))
7778

7879

80+
def win_encode(s):
81+
"""Encode unicodes for process arguments on Windows."""
82+
if isinstance(s, unicode):
83+
return s.encode(locale.getpreferredencoding(False))
84+
elif isinstance(s, bytes):
85+
return s
86+
elif s is not None:
87+
raise TypeError('Expected bytes or text, but got %r' % (s,))
88+
89+
7990
def with_metaclass(meta, *bases):
8091
"""copied from https://github.com/Byron/bcore/blob/master/src/python/butility/future.py#L15"""
8192
class metaclass(meta):

Diff for: git/index/base.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ def write(self, file_path=None, ignore_extension_data=False):
214214
self.entries
215215
lfd = LockedFD(file_path or self._file_path)
216216
stream = lfd.open(write=True, stream=True)
217-
ok = False
218217

218+
ok = False
219219
try:
220220
self._serialize(stream, ignore_extension_data)
221221
ok = True
@@ -602,14 +602,13 @@ def _store_path(self, filepath, fprogress):
602602
stream = None
603603
if S_ISLNK(st.st_mode):
604604
# in PY3, readlink is string, but we need bytes. In PY2, it's just OS encoded bytes, we assume UTF-8
605-
stream = BytesIO(force_bytes(os.readlink(filepath), encoding=defenc))
605+
open_stream = lambda: BytesIO(force_bytes(os.readlink(filepath), encoding=defenc))
606606
else:
607-
stream = open(filepath, 'rb')
608-
# END handle stream
609-
fprogress(filepath, False, filepath)
610-
istream = self.repo.odb.store(IStream(Blob.type, st.st_size, stream))
611-
fprogress(filepath, True, filepath)
612-
stream.close()
607+
open_stream = lambda: open(filepath, 'rb')
608+
with open_stream() as stream:
609+
fprogress(filepath, False, filepath)
610+
istream = self.repo.odb.store(IStream(Blob.type, st.st_size, stream))
611+
fprogress(filepath, True, filepath)
613612
return BaseIndexEntry((stat_mode_to_index_mode(st.st_mode),
614613
istream.binsha, 0, to_native_path_linux(filepath)))
615614

Diff for: git/objects/submodule/base.py

+12-9
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
)
3030
from git.compat import (
3131
string_types,
32-
defenc
32+
defenc,
33+
is_win,
3334
)
3435

3536
import stat
@@ -289,14 +290,16 @@ def _write_git_file_and_module_config(cls, working_tree_dir, module_abspath):
289290
"""
290291
git_file = os.path.join(working_tree_dir, '.git')
291292
rela_path = os.path.relpath(module_abspath, start=working_tree_dir)
292-
fp = open(git_file, 'wb')
293-
fp.write(("gitdir: %s" % rela_path).encode(defenc))
294-
fp.close()
295-
296-
writer = GitConfigParser(os.path.join(module_abspath, 'config'), read_only=False, merge_includes=False)
297-
writer.set_value('core', 'worktree',
298-
to_native_path_linux(os.path.relpath(working_tree_dir, start=module_abspath)))
299-
writer.release()
293+
if is_win:
294+
if os.path.isfile(git_file):
295+
os.remove(git_file)
296+
with open(git_file, 'wb') as fp:
297+
fp.write(("gitdir: %s" % rela_path).encode(defenc))
298+
299+
with GitConfigParser(os.path.join(module_abspath, 'config'),
300+
read_only=False, merge_includes=False) as writer:
301+
writer.set_value('core', 'worktree',
302+
to_native_path_linux(os.path.relpath(working_tree_dir, start=module_abspath)))
300303

301304
#{ Edit Interface
302305

Diff for: git/repo/base.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -899,12 +899,8 @@ def _clone(cls, git, url, path, odb_default_type, progress, **kwargs):
899899
try:
900900
proc = git.clone(url, path, with_extended_output=True, as_process=True,
901901
v=True, **add_progress(kwargs, git, progress))
902-
if progress:
903-
handle_process_output(proc, None, progress.new_message_handler(), finalize_process)
904-
else:
905-
(stdout, stderr) = proc.communicate()
906-
finalize_process(proc, stderr=stderr)
907-
# end handle progress
902+
progress_handler = progress and progress.new_message_handler() or None
903+
handle_process_output(proc, None, progress_handler, finalize_process)
908904
finally:
909905
if prev_cwd is not None:
910906
os.chdir(prev_cwd)

Diff for: git/test/test_index.py

+17-18
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,8 @@ def test_index_file_base(self):
113113
# write the data - it must match the original
114114
tmpfile = tempfile.mktemp()
115115
index_merge.write(tmpfile)
116-
fp = open(tmpfile, 'rb')
117-
self.assertEqual(fp.read(), fixture("index_merge"))
118-
fp.close()
116+
with open(tmpfile, 'rb') as fp:
117+
self.assertEqual(fp.read(), fixture("index_merge"))
119118
os.remove(tmpfile)
120119

121120
def _cmp_tree_index(self, tree, index):
@@ -329,22 +328,19 @@ def test_index_file_diffing(self, rw_repo):
329328
# reset the working copy as well to current head,to pull 'back' as well
330329
new_data = b"will be reverted"
331330
file_path = os.path.join(rw_repo.working_tree_dir, "CHANGES")
332-
fp = open(file_path, "wb")
333-
fp.write(new_data)
334-
fp.close()
331+
with open(file_path, "wb") as fp:
332+
fp.write(new_data)
335333
index.reset(rev_head_parent, working_tree=True)
336334
assert not index.diff(None)
337335
self.assertEqual(cur_branch, rw_repo.active_branch)
338336
self.assertEqual(cur_commit, rw_repo.head.commit)
339-
fp = open(file_path, 'rb')
340-
try:
337+
with open(file_path, 'rb') as fp:
341338
assert fp.read() != new_data
342-
finally:
343-
fp.close()
344339

345340
# test full checkout
346341
test_file = os.path.join(rw_repo.working_tree_dir, "CHANGES")
347-
open(test_file, 'ab').write(b"some data")
342+
with open(test_file, 'ab') as fd:
343+
fd.write(b"some data")
348344
rval = index.checkout(None, force=True, fprogress=self._fprogress)
349345
assert 'CHANGES' in list(rval)
350346
self._assert_fprogress([None])
@@ -369,9 +365,8 @@ def test_index_file_diffing(self, rw_repo):
369365

370366
# checkout file with modifications
371367
append_data = b"hello"
372-
fp = open(test_file, "ab")
373-
fp.write(append_data)
374-
fp.close()
368+
with open(test_file, "ab") as fp:
369+
fp.write(append_data)
375370
try:
376371
index.checkout(test_file)
377372
except CheckoutError as e:
@@ -380,7 +375,9 @@ def test_index_file_diffing(self, rw_repo):
380375
self.assertEqual(len(e.failed_files), len(e.failed_reasons))
381376
self.assertIsInstance(e.failed_reasons[0], string_types)
382377
self.assertEqual(len(e.valid_files), 0)
383-
assert open(test_file, 'rb').read().endswith(append_data)
378+
with open(test_file, 'rb') as fd:
379+
s = fd.read()
380+
self.assertTrue(s.endswith(append_data), s)
384381
else:
385382
raise AssertionError("Exception CheckoutError not thrown")
386383

@@ -639,9 +636,10 @@ def mixed_iterator():
639636
if is_win:
640637
# simlinks should contain the link as text ( which is what a
641638
# symlink actually is )
642-
open(fake_symlink_path, 'rb').read() == link_target
639+
with open(fake_symlink_path, 'rt') as fd:
640+
self.assertEqual(fd.read(), link_target)
643641
else:
644-
assert S_ISLNK(os.lstat(fake_symlink_path)[ST_MODE])
642+
self.assertTrue(S_ISLNK(os.lstat(fake_symlink_path)[ST_MODE]))
645643

646644
# TEST RENAMING
647645
def assert_mv_rval(rval):
@@ -691,7 +689,8 @@ def make_paths():
691689

692690
for fid in range(3):
693691
fname = 'newfile%i' % fid
694-
open(fname, 'wb').write(b"abcd")
692+
with open(fname, 'wb') as fd:
693+
fd.write(b"abcd")
695694
yield Blob(rw_repo, Blob.NULL_BIN_SHA, 0o100644, fname)
696695
# END for each new file
697696
# END path producer

0 commit comments

Comments
 (0)