borg-extract --progress

This commit is contained in:
Marian Beermann 2016-08-07 14:17:56 +02:00
parent 81dd381701
commit 8709cec57c
5 changed files with 42 additions and 7 deletions

View File

@ -422,7 +422,7 @@ Number of files: {0.stats.nfiles}'''.format(
return stats
def extract_item(self, item, restore_attrs=True, dry_run=False, stdout=False, sparse=False,
hardlink_masters=None, original_path=None):
hardlink_masters=None, original_path=None, pi=None):
"""
Extract archive item.
@ -433,11 +433,14 @@ Number of files: {0.stats.nfiles}'''.format(
:param sparse: write sparse files (chunk-granularity, independent of the original being sparse)
:param hardlink_masters: maps paths to (chunks, link_target) for extracting subtrees with hardlinks correctly
:param original_path: 'path' key as stored in archive
:param pi: ProgressIndicatorPercent (or similar) for file extraction progress (in bytes)
"""
has_damaged_chunks = 'chunks_healthy' in item
if dry_run or stdout:
if 'chunks' in item:
for _, data in self.pipeline.fetch_many([c.id for c in item.chunks], is_preloaded=True):
if pi:
pi.show(increase=len(data))
if stdout:
sys.stdout.buffer.write(data)
if stdout:
@ -489,6 +492,8 @@ Number of files: {0.stats.nfiles}'''.format(
with fd:
ids = [c.id for c in item.chunks]
for _, data in self.pipeline.fetch_many(ids, is_preloaded=True):
if pi:
pi.show(increase=len(data))
with backup_io():
if sparse and self.zeros.startswith(data):
# all-zero chunk: create a hole in a sparse file

View File

@ -41,6 +41,7 @@ from .helpers import log_multi
from .helpers import parse_pattern, PatternMatcher, PathPrefixPattern
from .helpers import signal_handler
from .helpers import ErrorIgnoringTextIOWrapper
from .helpers import ProgressIndicatorPercent
from .item import Item
from .key import key_creator, RepoKey, PassphraseKey
from .platform import get_flags
@ -439,6 +440,7 @@ class Archiver:
matcher, include_patterns = self.build_matcher(args.excludes, args.paths)
progress = args.progress
output_list = args.output_list
dry_run = args.dry_run
stdout = args.stdout
@ -453,6 +455,12 @@ class Archiver:
item.get('hardlink_master', True) and 'source' not in item)
filter = self.build_filter(matcher, item_is_hardlink_master, strip_components)
if progress:
extracted_size = sum(item.file_size() for item in archive.iter_items(filter))
pi = ProgressIndicatorPercent(total=extracted_size, msg='Extracting files %5.1f%%', step=0.1)
else:
pi = None
for item in archive.iter_items(filter, preload=True):
orig_path = item.path
if item_is_hardlink_master(item):
@ -472,19 +480,21 @@ class Archiver:
logging.getLogger('borg.output.list').info(remove_surrogates(orig_path))
try:
if dry_run:
archive.extract_item(item, dry_run=True)
archive.extract_item(item, dry_run=True, pi=pi)
else:
if stat.S_ISDIR(item.mode):
dirs.append(item)
archive.extract_item(item, restore_attrs=False)
else:
archive.extract_item(item, stdout=stdout, sparse=sparse, hardlink_masters=hardlink_masters,
original_path=orig_path)
original_path=orig_path, pi=pi)
except BackupOSError as e:
self.print_warning('%s: %s', remove_surrogates(orig_path), e)
if not args.dry_run:
pi = ProgressIndicatorPercent(total=len(dirs), msg='Setting directory permissions %3.0f%%', same_line=True)
while dirs:
pi.show()
dir_item = dirs.pop(-1)
try:
archive.extract_item(dir_item)
@ -1641,6 +1651,9 @@ class Archiver:
formatter_class=argparse.RawDescriptionHelpFormatter,
help='extract archive contents')
subparser.set_defaults(func=self.do_extract)
subparser.add_argument('-p', '--progress', dest='progress',
action='store_true', default=False,
help='show progress while extracting (may be slower)')
subparser.add_argument('--list', dest='output_list',
action='store_true', default=False,
help='output verbose list of items (files, dirs, ...)')

View File

@ -1142,17 +1142,17 @@ class ProgressIndicatorPercent:
self.logger.removeHandler(self.handler)
self.handler.close()
def progress(self, current=None):
def progress(self, current=None, increase=1):
if current is not None:
self.counter = current
pct = self.counter * 100 / self.total
self.counter += 1
self.counter += increase
if pct >= self.trigger_at:
self.trigger_at += self.step
return pct
def show(self, current=None):
pct = self.progress(current)
def show(self, current=None, increase=1):
pct = self.progress(current, increase)
if pct is not None:
return self.output(pct)

View File

@ -157,6 +157,14 @@ class Item(PropDict):
part = PropDict._make_property('part', int)
def file_size(self):
if 'chunks' not in self:
return 0
total_size = 0
for chunk_id, size, csize in self.chunks:
total_size += size
return total_size
class EncryptedKey(PropDict):
"""

View File

@ -751,6 +751,15 @@ class ArchiverTestCase(ArchiverTestCaseBase):
output = self.cmd('extract', '--list', '--info', self.repository_location + '::test')
self.assert_in("input/file", output)
def test_extract_progress(self):
self.cmd('init', self.repository_location)
self.create_regular_file('file', size=1024 * 80)
self.cmd('create', self.repository_location + '::test', 'input')
with changedir('output'):
output = self.cmd('extract', self.repository_location + '::test', '--progress')
assert 'Extracting files' in output
def _create_test_caches(self):
self.cmd('init', self.repository_location)
self.create_regular_file('file1', size=1024 * 80)