diff --git a/src/borg/archiver/extract_cmd.py b/src/borg/archiver/extract_cmd.py index d0fafc1d59..c7fc4b08c7 100644 --- a/src/borg/archiver/extract_cmd.py +++ b/src/borg/archiver/extract_cmd.py @@ -56,33 +56,50 @@ def do_extract(self, args, repository, manifest, archive): else: pi = None - for item in archive.iter_items(filter): - archive.preload_item_chunks(item, optimize_hardlinks=True) + for item in archive.iter_items(): 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)) + stripped_path = os.sep.join(orig_path.split(os.sep)[strip_components:]) + if not stripped_path: + continue + item.path = stripped_path + + is_matched = matcher.match(orig_path) + if output_list: - 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) + log_prefix = "+" if is_matched else "-" + logging.getLogger("borg.output.list").info(f"{log_prefix} {remove_surrogates(item.path)}") + + if is_matched: + 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)) + + 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() diff --git a/src/borg/testsuite/archiver/extract_cmd_test.py b/src/borg/testsuite/archiver/extract_cmd_test.py index b4f8d3175a..a6dd9632cf 100644 --- a/src/borg/testsuite/archiver/extract_cmd_test.py +++ b/src/borg/testsuite/archiver/extract_cmd_test.py @@ -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"