Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add flags to borg extract Command (Fixes #8564) #8575

Merged
merged 11 commits into from
Dec 27, 2024
Binary file added .coverage 2
Binary file not shown.
68 changes: 42 additions & 26 deletions src/borg/archiver/extract_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
matcher = build_matcher(args.patterns, args.paths)

progress = args.progress
output_list = args.output_list
dry_run = args.dry_run
stdout = args.stdout
sparse = args.sparse
Expand All @@ -56,33 +55,50 @@
else:
pi = None

for item in archive.iter_items(filter):
archive.preload_item_chunks(item, optimize_hardlinks=True)
for item in archive.iter_items(None):
alighazi288 marked this conversation as resolved.
Show resolved Hide resolved
orig_path = item.path
if strip_components:
item.path = os.sep.join(orig_path.split(os.sep)[strip_components:])
if not args.dry_run:
while dirs and not item.path.startswith(dirs[-1].path):
dir_item = dirs.pop(-1)
try:
archive.extract_item(dir_item, stdout=stdout)
except BackupError as e:
self.print_warning_instance(BackupWarning(remove_surrogates(dir_item.path), e))
if output_list:
alighazi288 marked this conversation as resolved.
Show resolved Hide resolved
logging.getLogger("borg.output.list").info(remove_surrogates(item.path))
try:
if dry_run:
archive.extract_item(item, dry_run=True, hlm=hlm, pi=pi)
else:
if stat.S_ISDIR(item.mode):
dirs.append(item)
archive.extract_item(item, stdout=stdout, restore_attrs=False)
components = orig_path.split(os.sep)
stripped_path = os.sep.join(components[strip_components:])

if not stripped_path:
continue
item.path = stripped_path
ThomasWaldmann marked this conversation as resolved.
Show resolved Hide resolved

is_matched = matcher.match(orig_path)
log_prefix = "+" if is_matched else "-"
alighazi288 marked this conversation as resolved.
Show resolved Hide resolved
logging.getLogger("borg.output.list").info(f"{log_prefix} {remove_surrogates(item.path)}")

if is_matched:
# Preloading item chunks only if the item will be fetched
archive.preload_item_chunks(item, optimize_hardlinks=True)

if not dry_run:
while dirs and not item.path.startswith(dirs[-1].path):
dir_item = dirs.pop(-1)
try:
archive.extract_item(dir_item, stdout=stdout)
except BackupError as e:
self.print_warning_instance(BackupWarning(remove_surrogates(dir_item.path), e))

Check warning on line 81 in src/borg/archiver/extract_cmd.py

View check run for this annotation

Codecov / codecov/patch

src/borg/archiver/extract_cmd.py#L80-L81

Added lines #L80 - L81 were not covered by tests

try:
if dry_run:
archive.extract_item(item, dry_run=True, hlm=hlm, pi=pi)
else:
archive.extract_item(
item, stdout=stdout, sparse=sparse, hlm=hlm, pi=pi, continue_extraction=continue_extraction
)
except BackupError as e:
self.print_warning_instance(BackupWarning(remove_surrogates(orig_path), e))
if stat.S_ISDIR(item.mode):
dirs.append(item)
archive.extract_item(item, stdout=stdout, restore_attrs=False)
else:
archive.extract_item(
item,
stdout=stdout,
sparse=sparse,
hlm=hlm,
pi=pi,
continue_extraction=continue_extraction,
)
except BackupError as e:
self.print_warning_instance(BackupWarning(remove_surrogates(orig_path), e))

if pi:
pi.finish()

Expand Down
19 changes: 19 additions & 0 deletions src/borg/testsuite/archiver/extract_cmd_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,3 +718,22 @@ def test_extract_continue(archivers, request):
assert f.read() == CONTENTS2
with open("input/file3", "rb") as f:
assert f.read() == CONTENTS3


def test_dry_run_extraction_flags(archivers, request):
archiver = request.getfixturevalue(archivers)
cmd(archiver, "repo-create", RK_ENCRYPTION)
create_regular_file(archiver.input_path, "file1", 0)
create_regular_file(archiver.input_path, "file2", 0)
create_regular_file(archiver.input_path, "file3", 0)
cmd(archiver, "create", "test", "input")

output = cmd(archiver, "extract", "--dry-run", "--list", "test", "-e", "input/file3")

expected_output = ["+ input/file1", "+ input/file2", "- input/file3"]
output_lines = output.splitlines()
for expected in expected_output:
assert expected in output_lines, f"Expected line not found: {expected}"
print(output)

assert not os.listdir("output"), "Output directory should be empty after dry-run"
Loading