浏览代码

Merge pull request #8344 from cr1901/relax_hardlink_assert

Do not assert on diff if hard link sources are not found due to exclusions
TW 8 月之前
父节点
当前提交
d94b954da2
共有 3 个文件被更改,包括 33 次插入5 次删除
  1. 10 4
      src/borg/archive.py
  2. 1 1
      src/borg/archiver.py
  3. 22 0
      src/borg/testsuite/archiver.py

+ 10 - 4
src/borg/archive.py

@@ -1038,7 +1038,7 @@ Utilization of max. archive size: {csize_max:.0%}
             logger.warning('borg check --repair is required to free all space.')
 
     @staticmethod
-    def compare_archives_iter(archive1, archive2, matcher=None, can_compare_chunk_ids=False, content_only=False):
+    def compare_archives_iter(print_warning, archive1, archive2, matcher=None, can_compare_chunk_ids=False, content_only=False):
         """
         Yields tuples with a path and an ItemDiff instance describing changes/indicating equality.
 
@@ -1117,10 +1117,16 @@ Utilization of max. archive size: {csize_max:.0%}
             update_hardlink_masters(deleted, deleted_item)
             yield (path, compare_items(deleted, deleted_item))
         for item1, item2 in deferred:
-            assert hardlink_master_seen(item1)
-            assert hardlink_master_seen(item2)
             assert item1.path == item2.path, "Deferred items have different paths"
-            yield (item1.path, compare_items(item1, item2))
+            hl_found1 = hardlink_master_seen(item1)
+            hl_found2 = hardlink_master_seen(item2)
+            if hl_found1 and hl_found2:
+                yield (item1.path, compare_items(item1, item2))
+            else:
+                if not hl_found1:
+                    print_warning(f"cannot find hardlink source for {item1.path} ({item1.source}), skipping compare.")
+                if not hl_found2:
+                    print_warning(f"cannot find hardlink source for {item2.path} ({item2.source}), skipping compare.")
 
 
 class MetadataCollector:

+ 1 - 1
src/borg/archiver.py

@@ -1162,7 +1162,7 @@ class Archiver:
 
         matcher = self.build_matcher(args.patterns, args.paths)
 
-        diffs = Archive.compare_archives_iter(archive1, archive2, matcher, can_compare_chunk_ids=can_compare_chunk_ids, content_only=args.content_only)
+        diffs = Archive.compare_archives_iter(self.print_warning, archive1, archive2, matcher, can_compare_chunk_ids=can_compare_chunk_ids, content_only=args.content_only)
         # 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)
 

+ 22 - 0
src/borg/testsuite/archiver.py

@@ -4536,6 +4536,8 @@ class ArchiverCorruptionTestCase(ArchiverTestCaseBase):
 
 
 class DiffArchiverTestCase(ArchiverTestCaseBase):
+    requires_hardlinks = pytest.mark.skipif(not are_hardlinks_supported(), reason='hardlinks not supported')
+
     def test_basic_functionality(self):
         # Setup files for the first snapshot
         self.create_regular_file('empty', size=0)
@@ -4838,6 +4840,26 @@ class DiffArchiverTestCase(ArchiverTestCaseBase):
             self.assert_not_in("ctime", output)
 
 
+    @requires_hardlinks
+    def test_multiple_link_exclusion(self):
+        path_a = os.path.join(self.input_path, 'a')
+        path_b = os.path.join(self.input_path, 'b')
+        os.mkdir(path_a)
+        os.mkdir(path_b)
+        hl_a = os.path.join(path_a, 'hardlink')
+        hl_b = os.path.join(path_b, 'hardlink')
+        self.create_regular_file(hl_a, contents=b'123456')
+        os.link(hl_a, hl_b)
+        self.cmd('init', '--encryption=repokey', self.repository_location)
+        self.cmd('create', self.repository_location + '::test0', 'input')
+        os.unlink(hl_a)  # Don't duplicate warning message- one is enough.
+        self.cmd('create', self.repository_location + '::test1', 'input')
+
+        output = self.cmd('diff', '--pattern=+ fm:input/b', '--pattern=! **/', self.repository_location + '::test0', 'test1', exit_code=EXIT_WARNING)
+        lines = output.splitlines()
+        self.assert_line_exists(lines, 'cannot find hardlink source for.*skipping compare.')
+
+
 def test_get_args():
     archiver = Archiver()
     # everything normal: