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

Suppress modified changes for files which weren't actually modified in JSON output. #8334

Merged
merged 3 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions src/borg/archiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -1121,11 +1121,22 @@ def item_to_tarinfo(item, original_path):
def do_diff(self, args, repository, manifest, key, archive):
"""Diff contents of two archives"""

def actual_change(j):
if j["type"] == "modified":
# Added/removed keys will not exist if chunker params differ
# between the two archives. Err on the side of caution and assume
# a real modification in this case (short-circuiting retrieving
# non-existent keys).
return not {"added", "removed"} <= j.keys() or not (j["added"] == 0 and j["removed"] == 0)
else:
# All other change types are indeed changes.
return True

def print_json_output(diff, path):
print(json.dumps({"path": path, "changes": [j for j, str in diff]}, sort_keys=True, cls=BorgJsonEncoder))
print(json.dumps({"path": path, "changes": [j for j, str in diff if actual_change(j)]}, sort_keys=True, cls=BorgJsonEncoder))

def print_text_output(diff, path):
print("{:<19} {}".format(' '.join([str for j, str in diff]), path))
print("{:<19} {}".format(' '.join([str for j, str in diff if actual_change(j)]), path))

print_output = print_json_output if args.json_lines else print_text_output

Expand Down
3 changes: 3 additions & 0 deletions src/borg/testsuite/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ def assert_dirs_equal(self, dir1, dir2, **kwargs):
def assert_line_exists(self, lines, expected_regexpr):
assert any(re.search(expected_regexpr, line) for line in lines), f"no match for {expected_regexpr} in {lines}"

def assert_line_not_exists(self, 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(self, diff, ignore_flags=False, ignore_xattrs=False, ignore_ns=False):
self.assert_equal(diff.left_only, [])
self.assert_equal(diff.right_only, [])
Expand Down
22 changes: 22 additions & 0 deletions src/borg/testsuite/archiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from hashlib import sha256
from io import BytesIO, StringIO
from unittest.mock import patch
from pathlib import Path

import pytest

Expand Down Expand Up @@ -4472,6 +4473,7 @@ def test_basic_functionality(self):
self.create_regular_file('file_removed', size=256)
self.create_regular_file('file_removed2', size=512)
self.create_regular_file('file_replaced', size=1024)
self.create_regular_file('file_touched', size=128)
os.mkdir('input/dir_replaced_with_file')
os.chmod('input/dir_replaced_with_file', stat.S_IFDIR | 0o755)
os.mkdir('input/dir_removed')
Expand Down Expand Up @@ -4500,6 +4502,7 @@ def test_basic_functionality(self):
self.create_regular_file('file_replaced', contents=b'0' * 4096)
os.unlink('input/file_removed')
os.unlink('input/file_removed2')
Path('input/file_touched').touch()
os.rmdir('input/dir_replaced_with_file')
self.create_regular_file('dir_replaced_with_file', size=8192)
os.chmod('input/dir_replaced_with_file', stat.S_IFREG | 0o755)
Expand Down Expand Up @@ -4562,6 +4565,15 @@ def do_asserts(output, can_compare_ids, content_only=False):
change = '0 B' if can_compare_ids else '{:<19}'.format('modified')
self.assert_line_exists(lines, f"{change}.*input/empty")

# Do not show a 0 byte change for a file whose contents weren't
ThomasWaldmann marked this conversation as resolved.
Show resolved Hide resolved
# modified.
self.assert_line_not_exists(lines, '0 B.*input/file_touched')
if not content_only:
self.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():
self.assert_line_exists(lines, f"{change}.*input/hardlink_contents_changed")
if are_symlinks_supported():
Expand Down Expand Up @@ -4610,6 +4622,16 @@ def get_changes(filename, data):
# File unchanged
assert not any(get_changes('input/file_unchanged', joutput))

# Do not show a 0 byte change for a file whose contents weren't
ThomasWaldmann marked this conversation as resolved.
Show resolved Hide resolved
# modified.
unexpected = {'type': 'modified', 'added': 0, 'removed': 0}
assert unexpected not in get_changes('input/file_touched', joutput)
if not content_only:
assert {"ctime", "mtime"}.issubset({c["type"] for c in get_changes('input/file_touched', joutput)})
else:
# And if we're doing content-only, don't show the file at all.
assert not any(get_changes('input/file_touched', joutput))

# Directory replaced with a regular file
if 'BORG_TESTS_IGNORE_MODES' not in os.environ and not content_only:
assert {'type': 'mode', 'old_mode': 'drwxr-xr-x', 'new_mode': '-rwxr-xr-x'} in \
Expand Down
Loading