Skip to content

Commit 863b386

Browse files
int3Byron
authored andcommitted
Add progress tracking for git-clone.
1 parent dfe3ba3 commit 863b386

File tree

3 files changed

+60
-47
lines changed

3 files changed

+60
-47
lines changed

‎git/remote.py

+9-41
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@
2424
TagReference
2525
)
2626

27-
from git.util import join_path
27+
from git.util import (
28+
join_path,
29+
_digest_process_messages,
30+
_finalize_proc
31+
)
2832
from gitdb.util import join
2933

3034
import re
@@ -432,42 +436,6 @@ def update(self, **kwargs):
432436
self.repo.git.remote("update", self.name)
433437
return self
434438

435-
def _digest_process_messages(self, fh, progress):
436-
"""Read progress messages from file-like object fh, supplying the respective
437-
progress messages to the progress instance.
438-
439-
:return: list(line, ...) list of lines without linebreaks that did
440-
not contain progress information"""
441-
line_so_far = ''
442-
dropped_lines = list()
443-
while True:
444-
char = fh.read(1)
445-
if not char:
446-
break
447-
448-
if char in ('\r', '\n'):
449-
dropped_lines.extend(progress._parse_progress_line(line_so_far))
450-
line_so_far = ''
451-
else:
452-
line_so_far += char
453-
# END process parsed line
454-
# END while file is not done reading
455-
return dropped_lines
456-
457-
458-
def _finalize_proc(self, proc):
459-
"""Wait for the process (fetch, pull or push) and handle its errors accordingly"""
460-
try:
461-
proc.wait()
462-
except GitCommandError,e:
463-
# if a push has rejected items, the command has non-zero return status
464-
# a return status of 128 indicates a connection error - reraise the previous one
465-
if proc.poll() == 128:
466-
raise
467-
pass
468-
# END exception handling
469-
470-
471439
def _get_fetch_info_from_stderr(self, proc, progress):
472440
# skip first line as it is some remote info we are not interested in
473441
output = IterableList('name')
@@ -477,7 +445,7 @@ def _get_fetch_info_from_stderr(self, proc, progress):
477445
# this also waits for the command to finish
478446
# Skip some progress lines that don't provide relevant information
479447
fetch_info_lines = list()
480-
for line in self._digest_process_messages(proc.stderr, progress):
448+
for line in _digest_process_messages(proc.stderr, progress):
481449
if line.startswith('From') or line.startswith('remote: Total'):
482450
continue
483451
elif line.startswith('warning:'):
@@ -499,15 +467,15 @@ def _get_fetch_info_from_stderr(self, proc, progress):
499467
output.extend(FetchInfo._from_line(self.repo, err_line, fetch_line)
500468
for err_line,fetch_line in zip(fetch_info_lines, fetch_head_info))
501469

502-
self._finalize_proc(proc)
470+
_finalize_proc(proc)
503471
return output
504472

505473
def _get_push_info(self, proc, progress):
506474
# read progress information from stderr
507475
# we hope stdout can hold all the data, it should ...
508476
# read the lines manually as it will use carriage returns between the messages
509477
# to override the previous one. This is why we read the bytes manually
510-
self._digest_process_messages(proc.stderr, progress)
478+
_digest_process_messages(proc.stderr, progress)
511479

512480
output = IterableList('name')
513481
for line in proc.stdout.readlines():
@@ -519,7 +487,7 @@ def _get_push_info(self, proc, progress):
519487
# END exception handling
520488
# END for each line
521489

522-
self._finalize_proc(proc)
490+
_finalize_proc(proc)
523491
return output
524492

525493

‎git/repo/base.py

+17-6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
)
1919

2020

21+
from git.util import (
22+
_digest_process_messages,
23+
_finalize_proc
24+
)
25+
2126
from gitdb.util import (
2227
join,
2328
isfile,
@@ -652,7 +657,7 @@ def init(cls, path=None, mkdir=True, **kwargs):
652657
return Repo(path)
653658

654659
@classmethod
655-
def _clone(cls, git, url, path, odb_default_type, **kwargs):
660+
def _clone(cls, git, url, path, odb_default_type, progress, **kwargs):
656661
# special handling for windows for path at which the clone should be
657662
# created.
658663
# tilde '~' will be expanded to the HOME no matter where the ~ occours. Hence
@@ -679,7 +684,10 @@ def _clone(cls, git, url, path, odb_default_type, **kwargs):
679684
# END windows handling
680685

681686
try:
682-
git.clone(url, path, **kwargs)
687+
proc = git.clone(url, path, with_extended_output=True, as_process=True, v=True, progress=True, **kwargs)
688+
if progress:
689+
_digest_process_messages(proc.stderr, progress)
690+
_finalize_proc(proc)
683691
finally:
684692
if prev_cwd is not None:
685693
os.chdir(prev_cwd)
@@ -703,28 +711,31 @@ def _clone(cls, git, url, path, odb_default_type, **kwargs):
703711
# END handle remote repo
704712
return repo
705713

706-
def clone(self, path, **kwargs):
714+
def clone(self, path, progress=None, **kwargs):
707715
"""Create a clone from this repository.
708716
:param path:
709717
is the full path of the new repo (traditionally ends with ./<name>.git).
710718
719+
:param progress: See 'git.remote.Remote.push'.
720+
711721
:param kwargs:
712722
odbt = ObjectDatabase Type, allowing to determine the object database
713723
implementation used by the returned Repo instance
714724
715725
All remaining keyword arguments are given to the git-clone command
716726
717727
:return: ``git.Repo`` (the newly cloned repo)"""
718-
return self._clone(self.git, self.git_dir, path, type(self.odb), **kwargs)
728+
return self._clone(self.git, self.git_dir, path, type(self.odb), progress, **kwargs)
719729

720730
@classmethod
721-
def clone_from(cls, url, to_path, **kwargs):
731+
def clone_from(cls, url, to_path, progress=None, **kwargs):
722732
"""Create a clone from the given URL
723733
:param url: valid git url, see http://www.kernel.org/pub/software/scm/git/docs/git-clone.html#URLS
724734
:param to_path: Path to which the repository should be cloned to
735+
:param progress: See 'git.remote.Remote.push'.
725736
:param kwargs: see the ``clone`` method
726737
:return: Repo instance pointing to the cloned directory"""
727-
return cls._clone(Git(os.getcwd()), url, to_path, GitCmdObjectDB, **kwargs)
738+
return cls._clone(Git(os.getcwd()), url, to_path, GitCmdObjectDB, progress, **kwargs)
728739

729740
def archive(self, ostream, treeish=None, prefix=None, **kwargs):
730741
"""Archive the tree at the given revision.

‎git/util.py

+34
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,40 @@ def get_user_id():
100100
# END get username from login
101101
return "%s@%s" % (username, platform.node())
102102

103+
def _digest_process_messages(fh, progress):
104+
"""Read progress messages from file-like object fh, supplying the respective
105+
progress messages to the progress instance.
106+
107+
:return: list(line, ...) list of lines without linebreaks that did
108+
not contain progress information"""
109+
line_so_far = ''
110+
dropped_lines = list()
111+
while True:
112+
char = fh.read(1)
113+
if not char:
114+
break
115+
116+
if char in ('\r', '\n'):
117+
dropped_lines.extend(progress._parse_progress_line(line_so_far))
118+
line_so_far = ''
119+
else:
120+
line_so_far += char
121+
# END process parsed line
122+
# END while file is not done reading
123+
return dropped_lines
124+
125+
def _finalize_proc(proc):
126+
"""Wait for the process (clone, fetch, pull or push) and handle its errors accordingly"""
127+
try:
128+
proc.wait()
129+
except GitCommandError,e:
130+
# if a push has rejected items, the command has non-zero return status
131+
# a return status of 128 indicates a connection error - reraise the previous one
132+
if proc.poll() == 128:
133+
raise
134+
pass
135+
# END exception handling
136+
103137
#} END utilities
104138

105139
#{ Classes

0 commit comments

Comments
 (0)