1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2025-01-01 12:45:34 +00:00

Ensure that 0B changes are hidden from text diffs too.

This commit is contained in:
William D. Jones 2024-08-18 22:37:38 -04:00 committed by Thomas Waldmann
parent 399c3f679d
commit 64e7095578
No known key found for this signature in database
GPG key ID: 243ACFA951F78E01
3 changed files with 48 additions and 21 deletions

View file

@ -19,17 +19,19 @@ class DiffMixIn:
def do_diff(self, args, repository, manifest): def do_diff(self, args, repository, manifest):
"""Diff contents of two archives""" """Diff contents of two archives"""
def print_json_output(diff): def actual_change(j):
def actual_change(j): j = j.to_dict()
j = j.to_dict() if j["type"] == "modified":
if j["type"] == "modified": # Added/removed keys will not exist if chunker params differ
# It's useful to show 0 added and 0 removed for text output # between the two archives. Err on the side of caution and assume
# but for JSON this is essentially noise. Additionally, the # a real modification in this case (short-circuiting retrieving
# JSON key is "changes", and this is not actually a change. # non-existent keys).
return not (j["added"] == 0 and j["removed"] == 0) return not {"added", "removed"} <= j.keys() or not (j["added"] == 0 and j["removed"] == 0)
else: else:
return True # All other change types are indeed changes.
return True
def print_json_output(diff):
print( print(
json.dumps( json.dumps(
{ {
@ -45,6 +47,17 @@ def actual_change(j):
) )
) )
def print_text_output(diff, formatter):
actual_changes = dict(
(name, change)
for name, change in diff.changes().items()
if actual_change(change) and (not args.content_only or (name not in DiffFormatter.METADATA))
)
diff._changes = actual_changes
res: str = formatter.format_item(diff)
if res.strip():
sys.stdout.write(res)
if args.format is not None: if args.format is not None:
format = args.format format = args.format
elif args.content_only: elif args.content_only:
@ -85,9 +98,7 @@ def actual_change(j):
if args.json_lines: if args.json_lines:
print_json_output(diff) print_json_output(diff)
else: else:
res: str = formatter.format_item(diff) print_text_output(diff, formatter)
if res.strip():
sys.stdout.write(res)
for pattern in matcher.get_unmatched_include_patterns(): for pattern in matcher.get_unmatched_include_patterns():
self.print_warning_instance(IncludePatternNeverMatchedWarning(pattern)) self.print_warning_instance(IncludePatternNeverMatchedWarning(pattern))

View file

@ -358,6 +358,12 @@ def assert_line_exists(lines, expected_regexpr):
assert any(re.search(expected_regexpr, line) for line in lines), f"no match for {expected_regexpr} in {lines}" assert any(re.search(expected_regexpr, line) for line in lines), f"no match for {expected_regexpr} in {lines}"
def assert_line_not_exists(lines, expected_regexpr):
assert not any(
re.search(expected_regexpr, line) for line in lines
), f"unexpected match for {expected_regexpr} in {lines}"
def _assert_dirs_equal_cmp(diff, ignore_flags=False, ignore_xattrs=False, ignore_ns=False): def _assert_dirs_equal_cmp(diff, ignore_flags=False, ignore_xattrs=False, ignore_ns=False):
assert diff.left_only == [] assert diff.left_only == []
assert diff.right_only == [] assert diff.right_only == []

View file

@ -7,7 +7,14 @@
from ...constants import * # NOQA from ...constants import * # NOQA
from .. import are_symlinks_supported, are_hardlinks_supported from .. import are_symlinks_supported, are_hardlinks_supported
from ...platformflags import is_win32, is_darwin from ...platformflags import is_win32, is_darwin
from . import cmd, create_regular_file, RK_ENCRYPTION, assert_line_exists, generate_archiver_tests from . import (
cmd,
create_regular_file,
RK_ENCRYPTION,
assert_line_exists,
generate_archiver_tests,
assert_line_not_exists,
)
pytest_generate_tests = lambda metafunc: generate_archiver_tests(metafunc, kinds="local,remote,binary") # NOQA pytest_generate_tests = lambda metafunc: generate_archiver_tests(metafunc, kinds="local,remote,binary") # NOQA
@ -105,17 +112,20 @@ def do_asserts(output, can_compare_ids, content_only=False):
# pointing to the file is not changed. # pointing to the file is not changed.
change = "modified.*0 B" if can_compare_ids else r"modified: \(can't get size\)" change = "modified.*0 B" if can_compare_ids else r"modified: \(can't get size\)"
assert_line_exists(lines, f"{change}.*input/empty") assert_line_exists(lines, f"{change}.*input/empty")
# Do not show a 0 byte change for a file whose contents weren't modified.
assert_line_not_exists(lines, "0 B.*input/file_touched")
if not content_only:
assert_line_exists(lines, "[cm]time:.*input/file_touched")
else:
# And if we're doing content-only, don't show the file at all.
assert "input/file_touched" not in output
if are_hardlinks_supported(): if are_hardlinks_supported():
assert_line_exists(lines, f"{change}.*input/hardlink_contents_changed") assert_line_exists(lines, f"{change}.*input/hardlink_contents_changed")
if are_symlinks_supported(): if are_symlinks_supported():
assert "input/link_target_contents_changed" not in output assert "input/link_target_contents_changed" not in output
# Show a 0 byte change for a file whose contents weren't modified for text output.
if content_only:
assert "input/file_touched" not in output
else:
assert_line_exists(lines, f"{change}.*input/file_touched")
# Added a new file and a hard link to it. Both links to the same # Added a new file and a hard link to it. Both links to the same
# inode should appear as separate files. # inode should appear as separate files.
assert "added: 2.05 kB input/file_added" in output assert "added: 2.05 kB input/file_added" in output
@ -159,7 +169,7 @@ def get_changes(filename, data):
# File unchanged # File unchanged
assert not any(get_changes("input/file_unchanged", joutput)) assert not any(get_changes("input/file_unchanged", joutput))
# Do NOT show a 0 byte change for a file whose contents weren't modified for JSON output. # Do not show a 0 byte change for a file whose contents weren't modified.
unexpected = {"type": "modified", "added": 0, "removed": 0} unexpected = {"type": "modified", "added": 0, "removed": 0}
assert unexpected not in get_changes("input/file_touched", joutput) assert unexpected not in get_changes("input/file_touched", joutput)
if not content_only: if not content_only: