summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Thiel <byronimo@gmail.com>2016-06-13 09:10:01 +0200
committerSebastian Thiel <byronimo@gmail.com>2016-06-13 09:10:01 +0200
commitda86442f6a7bf1263fb5aafdaf904ed2f7db839f (patch)
treeea368b8973aaceace5f2ef51188005d95be4bc9d
parentec830a25d39d4eb842ae016095ba257428772294 (diff)
parent6891caf73735ea465c909de8dc13129cc98c47f7 (diff)
downloadgitpython-da86442f6a7bf1263fb5aafdaf904ed2f7db839f.tar.gz
Merge branch 'pr-cmd-raise-with-stderr-on-error' of https://github.com/barry-scott/GitPython into barry-scott-pr-cmd-raise-with-stderr-on-error
-rw-r--r--git/cmd.py33
-rw-r--r--git/remote.py23
-rw-r--r--git/util.py14
3 files changed, 61 insertions, 9 deletions
diff --git a/git/cmd.py b/git/cmd.py
index a8afc144..633aedcb 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -69,6 +69,10 @@ else:
# Documentation
## @{
+def _drop_output_handler(line):
+ pass
+
+
def handle_process_output(process, stdout_handler, stderr_handler, finalizer):
"""Registers for notifications to lean that process output is ready to read, and dispatches lines to
the respective line handlers. We are able to handle carriage returns in case progress is sent by that
@@ -79,6 +83,13 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer):
:param stdout_handler: f(stdout_line_string), or None
:param stderr_hanlder: f(stderr_line_string), or None
:param finalizer: f(proc) - wait for proc to finish"""
+
+ log.debug('handle_process_output( process=%r, stdout_handler=%r, stderr_handler=%r, finalizer=%r'
+ % (process, stdout_handler, stderr_handler, finalizer))
+
+ if stdout_handler is None:
+ stdout_handler = _drop_output_handler
+
fdmap = {process.stdout.fileno(): (stdout_handler, [b'']),
process.stderr.fileno(): (stderr_handler, [b''])}
@@ -119,6 +130,7 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer):
# end single line helper
def _dispatch_lines(fno, handler, buf_list):
+ log.debug('fno=%d, handler=%r, buf_list=%r' % (fno, handler, buf_list))
lc = 0
for line in _read_lines_from_fno(fno, buf_list):
_dispatch_single_line(line, handler)
@@ -307,22 +319,35 @@ class Git(LazyMixin):
def __getattr__(self, attr):
return getattr(self.proc, attr)
- def wait(self, stderr=None):
+ def wait(self, stderr=b''):
"""Wait for the process and return its status code.
:param stderr: Previously read value of stderr, in case stderr is already closed.
:warn: may deadlock if output or error pipes are used and not handled separately.
:raise GitCommandError: if the return status is not 0"""
+
+ # stderr must be a bytes object as it will
+ # combined with more data from the process and
+ # decoded by the caller
+ if stderr is None:
+ stderr = b''
+ elif type(stderr) == unicode:
+ stderr = stderr.encode(defenc)
+
status = self.proc.wait()
def read_all_from_possibly_closed_stream(stream):
try:
- return stream.read()
+ last_stderr = stream.read()
+ if type(last_stderr) == unicode:
+ last_stderr = last_stderr.encode(defenc)
+ return stderr + last_stderr
except ValueError:
- return stderr or ''
+ return stderr or b''
if status != 0:
errstr = read_all_from_possibly_closed_stream(self.proc.stderr)
+ log.debug('AutoInterrupt wait stderr: %r' % (errstr,))
raise GitCommandError(self.args, status, errstr)
# END status handling
return status
@@ -609,7 +634,7 @@ class Git(LazyMixin):
bufsize=-1,
stdin=istream,
stderr=PIPE,
- stdout=PIPE if with_stdout else open(os.devnull, 'wb'),
+ stdout=PIPE,
shell=self.USE_SHELL,
close_fds=(os.name == 'posix'), # unsupported on windows
universal_newlines=universal_newlines,
diff --git a/git/remote.py b/git/remote.py
index 42753977..12a681b0 100644
--- a/git/remote.py
+++ b/git/remote.py
@@ -570,11 +570,16 @@ class Remote(LazyMixin, Iterable):
progress_handler = progress.new_message_handler()
+ error_message = None
+ stderr_text = None
+
for line in proc.stderr:
line = force_text(line)
for pline in progress_handler(line):
if line.startswith('fatal:') or line.startswith('error:'):
- raise GitCommandError(("Error when fetching: %s" % line,), 2)
+ error_message = "Error when fetching: %s" % (line,)
+ break
+
# END handle special messages
for cmd in cmds:
if len(line) > 1 and line[0] == ' ' and line[1] == cmd:
@@ -582,9 +587,19 @@ class Remote(LazyMixin, Iterable):
continue
# end find command code
# end for each comand code we know
+
+ if error_message is not None:
+ break
# end for each line progress didn't handle
+
+ if error_message is not None:
+ stderr_text = proc.stderr.read()
+
# end
- finalize_process(proc)
+ finalize_process(proc, stderr=stderr_text)
+
+ if error_message is not None:
+ raise GitCommandError(error_message, 2, stderr=stderr_text)
# read head information
fp = open(join(self.repo.git_dir, 'FETCH_HEAD'), 'rb')
@@ -631,6 +646,10 @@ class Remote(LazyMixin, Iterable):
try:
handle_process_output(proc, stdout_handler, progress_handler, finalize_process)
+ except GitCommandError as err:
+ # convert any error from wait() into the same error with stdout lines
+ raise GitCommandError(err.command, err.status, progress.get_stderr())
+
except Exception:
if len(output) == 0:
raise
diff --git a/git/util.py b/git/util.py
index 5ed014fc..706518b8 100644
--- a/git/util.py
+++ b/git/util.py
@@ -173,13 +173,17 @@ class RemoteProgress(object):
DONE_TOKEN = 'done.'
TOKEN_SEPARATOR = ', '
- __slots__ = ("_cur_line", "_seen_ops")
+ __slots__ = ("_cur_line", "_seen_ops", "_error_lines")
re_op_absolute = re.compile(r"(remote: )?([\w\s]+):\s+()(\d+)()(.*)")
re_op_relative = re.compile(r"(remote: )?([\w\s]+):\s+(\d+)% \((\d+)/(\d+)\)(.*)")
def __init__(self):
self._seen_ops = list()
self._cur_line = None
+ self._error_lines = []
+
+ def get_stderr(self):
+ return '\n'.join(self._error_lines)
def _parse_progress_line(self, line):
"""Parse progress information from the given line as retrieved by git-push
@@ -190,6 +194,10 @@ class RemoteProgress(object):
# Counting objects: 4, done.
# Compressing objects: 50% (1/2) \rCompressing objects: 100% (2/2) \rCompressing objects: 100% (2/2), done.
self._cur_line = line
+ if len(self._error_lines) > 0 or self._cur_line.startswith(('error:', 'fatal:')):
+ self._error_lines.append(self._cur_line)
+ return []
+
sub_lines = line.split('\r')
failed_lines = list()
for sline in sub_lines:
@@ -764,10 +772,10 @@ class WaitGroup(object):
self.cv.notify_all()
self.cv.release()
- def wait(self):
+ def wait(self, stderr=b''):
self.cv.acquire()
while self.count > 0:
- self.cv.wait()
+ self.cv.wait(strerr=stderr)
self.cv.release()