mirror of
https://github.com/borgbackup/borg.git
synced 2025-01-01 12:45:34 +00:00
Allows delete to be used with archive filters
This commit is contained in:
parent
17f2363935
commit
f2d4d36cea
2 changed files with 74 additions and 31 deletions
|
@ -770,11 +770,31 @@ def do_rename(self, args, repository, manifest, key, cache, archive):
|
|||
|
||||
@with_repository(exclusive=True, manifest=False)
|
||||
def do_delete(self, args, repository):
|
||||
"""Delete an existing repository or archive"""
|
||||
"""Delete an existing repository or archives"""
|
||||
if any((args.location.archive, args.first, args.last, args.prefix)):
|
||||
return self._delete_archives(args, repository)
|
||||
else:
|
||||
return self._delete_repository(args, repository)
|
||||
|
||||
def _delete_archives(self, args, repository):
|
||||
"""Delete archives"""
|
||||
manifest, key = Manifest.load(repository)
|
||||
|
||||
if args.location.archive:
|
||||
manifest, key = Manifest.load(repository)
|
||||
with Cache(repository, key, manifest, lock_wait=self.lock_wait) as cache:
|
||||
archive = Archive(repository, key, manifest, args.location.archive, cache=cache)
|
||||
archive_names = (args.location.archive,)
|
||||
else:
|
||||
archive_names = tuple(x.name for x in self._get_filtered_archives(args, manifest))
|
||||
if not archive_names:
|
||||
return self.exit_code
|
||||
|
||||
stats_logger = logging.getLogger('borg.output.stats')
|
||||
if args.stats:
|
||||
log_multi(DASHES, STATS_HEADER, logger=stats_logger)
|
||||
|
||||
with Cache(repository, key, manifest, lock_wait=self.lock_wait) as cache:
|
||||
for i, archive_name in enumerate(archive_names, 1):
|
||||
logger.info('Deleting {} ({}/{}):'.format(archive_name, i, len(archive_names)))
|
||||
archive = Archive(repository, key, manifest, archive_name, cache=cache)
|
||||
stats = Statistics()
|
||||
archive.delete(stats, progress=args.progress, forced=args.forced)
|
||||
manifest.write()
|
||||
|
@ -782,33 +802,41 @@ def do_delete(self, args, repository):
|
|||
cache.commit()
|
||||
logger.info("Archive deleted.")
|
||||
if args.stats:
|
||||
log_multi(DASHES,
|
||||
STATS_HEADER,
|
||||
stats.summary.format(label='Deleted data:', stats=stats),
|
||||
str(cache),
|
||||
DASHES, logger=logging.getLogger('borg.output.stats'))
|
||||
else:
|
||||
if not args.cache_only:
|
||||
msg = []
|
||||
try:
|
||||
manifest, key = Manifest.load(repository)
|
||||
except NoManifestError:
|
||||
msg.append("You requested to completely DELETE the repository *including* all archives it may contain.")
|
||||
msg.append("This repository seems to have no manifest, so we can't tell anything about its contents.")
|
||||
else:
|
||||
msg.append("You requested to completely DELETE the repository *including* all archives it contains:")
|
||||
for archive_info in manifest.archives.list(sort_by='ts'):
|
||||
msg.append(format_archive(archive_info))
|
||||
msg.append("Type 'YES' if you understand this and want to continue: ")
|
||||
msg = '\n'.join(msg)
|
||||
if not yes(msg, false_msg="Aborting.", invalid_msg='Invalid answer, aborting.', truish=('YES', ),
|
||||
retry=False, env_var_override='BORG_DELETE_I_KNOW_WHAT_I_AM_DOING'):
|
||||
self.exit_code = EXIT_ERROR
|
||||
return self.exit_code
|
||||
repository.destroy()
|
||||
logger.info("Repository deleted.")
|
||||
Cache.destroy(repository)
|
||||
logger.info("Cache deleted.")
|
||||
log_multi(stats.summary.format(label='Deleted data:', stats=stats),
|
||||
DASHES, logger=stats_logger)
|
||||
if not args.forced and self.exit_code:
|
||||
break
|
||||
if args.stats:
|
||||
stats_logger.info(str(cache))
|
||||
|
||||
return self.exit_code
|
||||
|
||||
def _delete_repository(self, args, repository):
|
||||
"""Delete a repository"""
|
||||
if not args.cache_only:
|
||||
msg = []
|
||||
try:
|
||||
manifest, key = Manifest.load(repository)
|
||||
except NoManifestError:
|
||||
msg.append("You requested to completely DELETE the repository *including* all archives it may "
|
||||
"contain.")
|
||||
msg.append("This repository seems to have no manifest, so we can't tell anything about its "
|
||||
"contents.")
|
||||
else:
|
||||
msg.append("You requested to completely DELETE the repository *including* all archives it "
|
||||
"contains:")
|
||||
for archive_info in manifest.archives.list(sort_by='ts'):
|
||||
msg.append(format_archive(archive_info))
|
||||
msg.append("Type 'YES' if you understand this and want to continue: ")
|
||||
msg = '\n'.join(msg)
|
||||
if not yes(msg, false_msg="Aborting.", invalid_msg='Invalid answer, aborting.', truish=('YES',),
|
||||
retry=False, env_var_override='BORG_DELETE_I_KNOW_WHAT_I_AM_DOING'):
|
||||
self.exit_code = EXIT_ERROR
|
||||
return self.exit_code
|
||||
repository.destroy()
|
||||
logger.info("Repository deleted.")
|
||||
Cache.destroy(repository)
|
||||
logger.info("Cache deleted.")
|
||||
return self.exit_code
|
||||
|
||||
@with_repository()
|
||||
|
@ -1969,6 +1997,7 @@ def build_parser(self, prog=None):
|
|||
subparser.add_argument('location', metavar='TARGET', nargs='?', default='',
|
||||
type=location_validator(),
|
||||
help='archive or repository to delete')
|
||||
self.add_archives_filters_args(subparser)
|
||||
|
||||
list_epilog = textwrap.dedent("""
|
||||
This command lists the contents of a repository or an archive.
|
||||
|
|
|
@ -59,6 +59,9 @@ def exec_cmd(*args, archiver=None, fork=False, exe=None, **kw):
|
|||
except subprocess.CalledProcessError as e:
|
||||
output = e.output
|
||||
ret = e.returncode
|
||||
except SystemExit as e: # possibly raised by argparse
|
||||
output = ''
|
||||
ret = e.code
|
||||
return ret, os.fsdecode(output)
|
||||
else:
|
||||
stdin, stdout, stderr = sys.stdin, sys.stdout, sys.stderr
|
||||
|
@ -987,8 +990,13 @@ def test_delete(self):
|
|||
self.cmd('init', self.repository_location)
|
||||
self.cmd('create', self.repository_location + '::test', 'input')
|
||||
self.cmd('create', self.repository_location + '::test.2', 'input')
|
||||
self.cmd('create', self.repository_location + '::test.3', 'input')
|
||||
self.cmd('create', self.repository_location + '::another_test.1', 'input')
|
||||
self.cmd('create', self.repository_location + '::another_test.2', 'input')
|
||||
self.cmd('extract', '--dry-run', self.repository_location + '::test')
|
||||
self.cmd('extract', '--dry-run', self.repository_location + '::test.2')
|
||||
self.cmd('delete', '--prefix', 'another_', self.repository_location)
|
||||
self.cmd('delete', '--last', '1', self.repository_location)
|
||||
self.cmd('delete', self.repository_location + '::test')
|
||||
self.cmd('extract', '--dry-run', self.repository_location + '::test.2')
|
||||
output = self.cmd('delete', '--stats', self.repository_location + '::test.2')
|
||||
|
@ -1811,6 +1819,12 @@ def test_recreate_list_output(self):
|
|||
self.assert_not_in("input/file1", output)
|
||||
self.assert_not_in("x input/file5", output)
|
||||
|
||||
def test_bad_filters(self):
|
||||
self.cmd('init', self.repository_location)
|
||||
self.cmd('create', self.repository_location + '::test', 'input')
|
||||
self.cmd('delete', '--first', '1', '--last', '1', self.repository_location, fork=True, exit_code=2)
|
||||
|
||||
|
||||
def test_key_export_keyfile(self):
|
||||
export_file = self.output_path + '/exported'
|
||||
self.cmd('init', self.repository_location, '--encryption', 'keyfile')
|
||||
|
|
Loading…
Reference in a new issue