1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2025-03-09 13:53:09 +00:00

Merge pull request #8334 from cr1901/json-no-changes

Suppress modified changes for files which weren't actually modified in JSON output.
This commit is contained in:
TW 2024-08-21 18:45:52 +02:00 committed by GitHub
commit 0ebb80c05d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 38 additions and 2 deletions

View file

@ -1121,11 +1121,22 @@ class Archiver:
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

View file

@ -191,6 +191,9 @@ class BaseTestCase(unittest.TestCase):
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, [])

View file

@ -23,6 +23,7 @@ from datetime import timedelta
from hashlib import sha256
from io import BytesIO, StringIO
from unittest.mock import patch
from pathlib import Path
import pytest
@ -4472,6 +4473,7 @@ class DiffArchiverTestCase(ArchiverTestCaseBase):
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')
@ -4500,6 +4502,7 @@ class DiffArchiverTestCase(ArchiverTestCaseBase):
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)
@ -4562,6 +4565,15 @@ class DiffArchiverTestCase(ArchiverTestCaseBase):
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
# 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():
@ -4610,6 +4622,16 @@ class DiffArchiverTestCase(ArchiverTestCaseBase):
# 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
# 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 \