move diff command to archiver.diff

This commit is contained in:
Thomas Waldmann 2022-07-08 22:27:48 +02:00
parent ea03562b11
commit 9fb224db6b
3 changed files with 131 additions and 101 deletions

View File

@ -121,6 +121,7 @@ per_file_ignores =
src/borg/archiver/config.py:F405,E722
src/borg/archiver/common.py:E501,F405
src/borg/archiver/debug.py:F405
src/borg/archiver/diff.py:F405
src/borg/archiver/help.py:E501,F405
src/borg/archiver/keys.py:F405
src/borg/archiver/prune.py:F405

View File

@ -100,6 +100,7 @@ def get_func(args):
from .benchmarks import BenchmarkMixIn
from .config import ConfigMixIn
from .debug import DebugMixIn
from .diff import DiffMixIn
from .help import HelpMixIn
from .keys import KeysMixIn
from .locks import LocksMixIn
@ -109,7 +110,16 @@ from .transfer import TransferMixIn
class Archiver(
ConfigMixIn, DebugMixIn, TarMixIn, BenchmarkMixIn, KeysMixIn, LocksMixIn, PruneMixIn, HelpMixIn, TransferMixIn
ConfigMixIn,
DebugMixIn,
DiffMixIn,
TarMixIn,
BenchmarkMixIn,
KeysMixIn,
LocksMixIn,
PruneMixIn,
HelpMixIn,
TransferMixIn,
):
def __init__(self, lock_wait=None, prog=None):
self.exit_code = EXIT_SUCCESS
@ -774,50 +784,6 @@ class Archiver(
pi.finish()
return self.exit_code
@with_repository(compatibility=(Manifest.Operation.READ,))
@with_archive
def do_diff(self, args, repository, manifest, key, archive):
"""Diff contents of two archives"""
def print_json_output(diff, path):
print(json.dumps({"path": path, "changes": [j for j, str in diff]}))
def print_text_output(diff, path):
print("{:<19} {}".format(" ".join([str for j, str in diff]), path))
print_output = print_json_output if args.json_lines else print_text_output
archive1 = archive
archive2 = Archive(repository, key, manifest, args.other_name, consider_part_files=args.consider_part_files)
can_compare_chunk_ids = (
archive1.metadata.get("chunker_params", False) == archive2.metadata.get("chunker_params", True)
or args.same_chunker_params
)
if not can_compare_chunk_ids:
self.print_warning(
"--chunker-params might be different between archives, diff will be slow.\n"
"If you know for certain that they are the same, pass --same-chunker-params "
"to override this check."
)
matcher = self.build_matcher(args.patterns, args.paths)
diffs = Archive.compare_archives_iter(archive1, archive2, matcher, can_compare_chunk_ids=can_compare_chunk_ids)
# Conversion to string and filtering for diff.equal to save memory if sorting
diffs = ((path, diff.changes()) for path, diff in diffs if not diff.equal)
if args.sort:
diffs = sorted(diffs)
for path, diff in diffs:
print_output(diff, path)
for pattern in matcher.get_unmatched_include_patterns():
self.print_warning("Include pattern '%s' never matched.", pattern)
return self.exit_code
@with_repository(exclusive=True, cache=True, compatibility=(Manifest.Operation.CHECK,))
@with_archive
def do_rename(self, args, repository, manifest, key, cache, archive):
@ -1627,6 +1593,7 @@ class Archiver(
subparsers = parser.add_subparsers(title="required arguments", metavar="<command>")
self.build_parser_benchmarks(subparsers, common_parser, mid_common_parser)
self.build_parser_diff(subparsers, common_parser, mid_common_parser)
self.build_parser_locks(subparsers, common_parser, mid_common_parser)
self.build_parser_prune(subparsers, common_parser, mid_common_parser)
@ -2287,62 +2254,6 @@ class Archiver(
)
define_archive_filters_group(subparser)
# borg diff
diff_epilog = process_epilog(
"""
This command finds differences (file contents, user/group/mode) between archives.
A repository location and an archive name must be specified for REPO::ARCHIVE1.
ARCHIVE2 is just another archive name in same repository (no repository location
allowed).
For archives created with Borg 1.1 or newer diff automatically detects whether
the archives are created with the same chunker params. If so, only chunk IDs
are compared, which is very fast.
For archives prior to Borg 1.1 chunk contents are compared by default.
If you did not create the archives with different chunker params,
pass ``--same-chunker-params``.
Note that the chunker params changed from Borg 0.xx to 1.0.
For more help on include/exclude patterns, see the :ref:`borg_patterns` command output.
"""
)
subparser = subparsers.add_parser(
"diff",
parents=[common_parser],
add_help=False,
description=self.do_diff.__doc__,
epilog=diff_epilog,
formatter_class=argparse.RawDescriptionHelpFormatter,
help="find differences in archive contents",
)
subparser.set_defaults(func=self.do_diff)
subparser.add_argument(
"--numeric-ids",
dest="numeric_ids",
action="store_true",
help="only consider numeric user and group identifiers",
)
subparser.add_argument(
"--same-chunker-params",
dest="same_chunker_params",
action="store_true",
help="Override check of chunker parameters.",
)
subparser.add_argument("--sort", dest="sort", action="store_true", help="Sort the output lines by file path.")
subparser.add_argument("--json-lines", action="store_true", help="Format output as JSON Lines. ")
subparser.add_argument("name", metavar="ARCHIVE1", type=archivename_validator(), help="ARCHIVE1 name")
subparser.add_argument("other_name", metavar="ARCHIVE2", type=archivename_validator(), help="ARCHIVE2 name")
subparser.add_argument(
"paths",
metavar="PATH",
nargs="*",
type=str,
help="paths of items inside the archives to compare; patterns are supported",
)
define_exclusion_group(subparser)
# borg extract
extract_epilog = process_epilog(
"""

118
src/borg/archiver/diff.py Normal file
View File

@ -0,0 +1,118 @@
import argparse
import json
from .common import with_repository, with_archive
from ..archive import Archive
from ..constants import * # NOQA
from ..helpers import archivename_validator
from ..helpers import Manifest
from ..logger import create_logger
logger = create_logger()
class DiffMixIn:
@with_repository(compatibility=(Manifest.Operation.READ,))
@with_archive
def do_diff(self, args, repository, manifest, key, archive):
"""Diff contents of two archives"""
def print_json_output(diff, path):
print(json.dumps({"path": path, "changes": [j for j, str in diff]}))
def print_text_output(diff, path):
print("{:<19} {}".format(" ".join([str for j, str in diff]), path))
print_output = print_json_output if args.json_lines else print_text_output
archive1 = archive
archive2 = Archive(repository, key, manifest, args.other_name, consider_part_files=args.consider_part_files)
can_compare_chunk_ids = (
archive1.metadata.get("chunker_params", False) == archive2.metadata.get("chunker_params", True)
or args.same_chunker_params
)
if not can_compare_chunk_ids:
self.print_warning(
"--chunker-params might be different between archives, diff will be slow.\n"
"If you know for certain that they are the same, pass --same-chunker-params "
"to override this check."
)
matcher = self.build_matcher(args.patterns, args.paths)
diffs = Archive.compare_archives_iter(archive1, archive2, matcher, can_compare_chunk_ids=can_compare_chunk_ids)
# Conversion to string and filtering for diff.equal to save memory if sorting
diffs = ((path, diff.changes()) for path, diff in diffs if not diff.equal)
if args.sort:
diffs = sorted(diffs)
for path, diff in diffs:
print_output(diff, path)
for pattern in matcher.get_unmatched_include_patterns():
self.print_warning("Include pattern '%s' never matched.", pattern)
return self.exit_code
def build_parser_diff(self, subparsers, common_parser, mid_common_parser):
from .common import process_epilog
from .common import define_exclusion_group
diff_epilog = process_epilog(
"""
This command finds differences (file contents, user/group/mode) between archives.
A repository location and an archive name must be specified for REPO::ARCHIVE1.
ARCHIVE2 is just another archive name in same repository (no repository location
allowed).
For archives created with Borg 1.1 or newer diff automatically detects whether
the archives are created with the same chunker params. If so, only chunk IDs
are compared, which is very fast.
For archives prior to Borg 1.1 chunk contents are compared by default.
If you did not create the archives with different chunker params,
pass ``--same-chunker-params``.
Note that the chunker params changed from Borg 0.xx to 1.0.
For more help on include/exclude patterns, see the :ref:`borg_patterns` command output.
"""
)
subparser = subparsers.add_parser(
"diff",
parents=[common_parser],
add_help=False,
description=self.do_diff.__doc__,
epilog=diff_epilog,
formatter_class=argparse.RawDescriptionHelpFormatter,
help="find differences in archive contents",
)
subparser.set_defaults(func=self.do_diff)
subparser.add_argument(
"--numeric-ids",
dest="numeric_ids",
action="store_true",
help="only consider numeric user and group identifiers",
)
subparser.add_argument(
"--same-chunker-params",
dest="same_chunker_params",
action="store_true",
help="Override check of chunker parameters.",
)
subparser.add_argument("--sort", dest="sort", action="store_true", help="Sort the output lines by file path.")
subparser.add_argument("--json-lines", action="store_true", help="Format output as JSON Lines. ")
subparser.add_argument("name", metavar="ARCHIVE1", type=archivename_validator(), help="ARCHIVE1 name")
subparser.add_argument("other_name", metavar="ARCHIVE2", type=archivename_validator(), help="ARCHIVE2 name")
subparser.add_argument(
"paths",
metavar="PATH",
nargs="*",
type=str,
help="paths of items inside the archives to compare; patterns are supported",
)
define_exclusion_group(subparser)