| 
					
				 | 
			
			
				@@ -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( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             """ 
			 |