Skip to content

Commit b38107c

Browse files
Merge pull request #9422 from ThomasWaldmann/deletion-fixes
prune/delete fixes
2 parents f3ac2e0 + 8a4f42d commit b38107c

File tree

3 files changed

+29
-8
lines changed

3 files changed

+29
-8
lines changed

src/borg/archiver/delete_cmd.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ def do_delete(self, args, repository):
3636
logger_list = logging.getLogger("borg.output.list")
3737
for i, archive_info in enumerate(archive_infos, 1):
3838
name, id, hex_id = archive_info.name, archive_info.id, bin_to_hex(archive_info.id)
39+
# format early before deletion of the archive
40+
archive_formatted = format_archive(archive_info)
3941
try:
4042
# this does NOT use Archive.delete, so this code hopefully even works in cases a corrupt archive
4143
# would make the code in class Archive crash, so the user can at least get rid of such archives.
@@ -47,7 +49,7 @@ def do_delete(self, args, repository):
4749
deleted = True
4850
if self.output_list:
4951
msg = "Would delete: {} ({}/{})" if dry_run else "Deleted archive: {} ({}/{})"
50-
logger_list.info(msg.format(format_archive(archive_info), i, count))
52+
logger_list.info(msg.format(archive_formatted, i, count))
5153
if dry_run:
5254
logger.info("Finished dry-run.")
5355
elif deleted:

src/borg/archiver/prune_cmd.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -179,29 +179,32 @@ def do_prune(self, args, repository, manifest):
179179
archives_deleted = 0
180180
uncommitted_deletes = 0
181181
pi = ProgressIndicatorPercent(total=len(to_delete), msg="Pruning archives %3.0f%%", msgid="prune")
182-
for archive in archives:
182+
for archive_info in archives:
183183
if sig_int and sig_int.action_done():
184184
break
185-
if archive in to_delete:
185+
# format_item may internally load the archive from the repository,
186+
# so we must call it before deleting the archive.
187+
archive_formatted = formatter.format_item(archive_info, jsonline=False)
188+
if archive_info in to_delete:
186189
pi.show()
187190
if args.dry_run:
188191
log_message = "Would prune:"
189192
else:
190193
archives_deleted += 1
191194
log_message = "Pruning archive (%d/%d):" % (archives_deleted, to_delete_len)
192-
archive = Archive(manifest, archive.id, cache=cache)
195+
archive = Archive(manifest, archive_info.id, cache=cache)
193196
archive.delete()
194197
uncommitted_deletes += 1
195198
else:
196199
log_message = "Keeping archive (rule: {rule} #{num}):".format(
197-
rule=kept_because[archive.id][0], num=kept_because[archive.id][1]
200+
rule=kept_because[archive_info.id][0], num=kept_because[archive_info.id][1]
198201
)
199202
if (
200203
args.output_list
201-
or (args.list_pruned and archive in to_delete)
202-
or (args.list_kept and archive not in to_delete)
204+
or (args.list_pruned and archive_info in to_delete)
205+
or (args.list_kept and archive_info not in to_delete)
203206
):
204-
list_logger.info(f"{log_message:<44} {formatter.format_item(archive, jsonline=False)}")
207+
list_logger.info(f"{log_message:<44} {archive_formatted}")
205208
pi.finish()
206209
if sig_int:
207210
raise Error("Got Ctrl-C / SIGINT.")

src/borg/testsuite/archiver/prune_cmd_test.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,3 +400,19 @@ def test_prune_split_no_archives():
400400

401401
assert keep == []
402402
assert kept_because == {}
403+
404+
405+
def test_prune_list_with_metadata_format(archivers, request):
406+
# Regression test for: prune --list with a format string that requires loading
407+
# archive metadata (e.g. {hostname}) must not fail when archives are deleted.
408+
# The bug was that format_item() was called after archive.delete(), causing
409+
# Archive.DoesNotExist when the formatter tried to lazy-load the archive.
410+
archiver = request.getfixturevalue(archivers)
411+
cmd(archiver, "repo-create", RK_ENCRYPTION)
412+
cmd(archiver, "create", "test1", src_dir)
413+
cmd(archiver, "create", "test2", src_dir)
414+
# {hostname} is a "call key" that triggers lazy loading of the archive from the repo.
415+
# With the buggy code this would raise Archive.DoesNotExist for the pruned archive.
416+
output = cmd(archiver, "prune", "--list", "--keep-daily=1", "--format={name} {hostname}{NL}")
417+
assert "test1" in output
418+
assert "test2" in output

0 commit comments

Comments
 (0)